.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SWUPDATE" "1" "May 24, 2023" "2023.05" "Embedded Software Update Documentation" .SH NAME swupdate \- SWUpdate Documentation [image] .sp SWUpdate provides a reliable way to update the software on an embedded system. Sources are hosted at \fI\%https://github.com/sbabic/swupdate\fP\&. Do not forget to \fIstar\fP SWUpdate. .SH SOFTWARE MANAGEMENT ON EMBEDDED SYSTEMS .sp As Embedded Systems become more and more complex, their software reflects the augmented complexity. It is vital that the software on an embedded system can be updated in an absolutely reliable way, as new features and fixes are added. .sp On a Linux\-based system, we can find in most cases the following elements: .INDENT 0.0 .IP \(bu 2 the boot loader. .IP \(bu 2 the kernel and the DT (Device Tree) file. .IP \(bu 2 the root file system .IP \(bu 2 other file systems, mounted at a later point .IP \(bu 2 customer data, in raw format or on a file system .IP \(bu 2 application specific software. For example, firmware to be downloaded on connected micro\-controllers, and so on. .UNINDENT .sp Generally speaking, in most cases it is required to update kernel and root file system, preserving user data \- but cases vary. .sp In only a few cases it is required to update the boot loader, too. In fact, updating the boot loader is quite always risky, because a failure in the update breaks the board. Restoring a broken board is possible in some cases, but this is not left in most cases to the end user and the system must be sent back to the manufacturer. .sp There are a lot of different concepts about updating the software. I like to expose some of them, and then explain why I have implemented this project. .SS Updating through the boot loader .sp Boot loaders do much more as simply start the kernel. They have their own shell and can be managed using a processor’s peripheral, in most cases a serial line. They are often script\-able, letting possible to implement some kind of software update mechanism. .sp However, I found some drawbacks in this approach, that let me search for another solution, based on an application running on Linux: .SS Boot loaders have limited access to peripherals .sp Not all peripherals supported by the kernel are available with the boot loader. When it makes sense to add support to the kernel, because the peripheral is then available by the main application, it does not always make sense to duplicate the effort to port the driver to the boot loader. .SS Boot loader’s drivers are not updated .sp Boot loader’s drivers are mostly ported from the Linux kernel, but due to adaptations they are not later fixed or synchronized with the kernel, while bug fixes flow regularly in the Linux kernel. Some peripherals can then work in a not reliable ways, and fixing the issues can be not easy. Drivers in boot loaders are more or less a fork of the respective drivers in kernel. .sp As example, the UBI / UBIFS for NAND devices contains a lot of fixes in the kernel, that are not ported back to the boot loaders. The same can be found for the USB stack. The effort to support new peripherals or protocols is better to be used for the kernel as for the boot loaders. .SS Reduced file systems .sp The number of supported file systems is limited and porting a file system to the boot loader requires high effort. .SS Network support is limited .sp Network stack is limited, generally an update is possible via UDP but not via TCP. .SS Interaction with the operator .sp It is difficult to expose an interface to the operator, such as a GUI with a browser or on a display. .sp A complex logic can be easier implemented inside an application else in the boot loader. Extending the boot loader becomes complicated because the whole range of services and libraries are not available. .SS Boot loader’s update advantages .sp However, this approach has some advantages, too: .INDENT 0.0 .IP \(bu 2 software for update is generally simpler. \- smaller footprint: a stand\-alone application only for software management requires an own kernel and a root file system. Even if their size can be trimmed dropping what is not required for updating the software, their size is not negligible. .UNINDENT .SS Updating through a package manager .sp All Linux distributions are updating with a package manager. Why is it not suitable for embedded ? .sp I cannot say it cannot be used, but there is an important drawback using this approach. Embedded systems are well tested with a specific software. Using a package manager can put weirdness because the software itself is not anymore \fIatomic\fP, but split into a long list of packages. How can we be assured that an application with library version x.y works, and also with different versions of the same library? How can it be successfully tested? .sp For a manufacturer, it is generally better to say that a new release of software (well tested by its test engineers) is released, and the new software (or firmware) is available for updating. Splitting in packages can generate nightmare and high effort for the testers. .sp The ease of replacing single files can speed up the development, but it is a software\-versions nightmare at the customer site. If a customer report a bug, how can it is possible that software is “version 2.5” when a patch for some files were sent previously to the customer ? .sp An atomic update is generally a must feature for an embedded system. .SS Strategies for an application doing software upgrade .sp Instead of using the boot loader, an application can take into charge to upgrade the system. The application can use all services provided by the OS. The proposed solution is a stand\-alone software, that follow customer rules and performs checks to determine if a software is installable, and then install the software on the desired storage. .sp The application can detect if the provided new software is suitable for the hardware, and it is can also check if the software is released by a verified authority. The range of features can grow from small system to a complex one, including the possibility to have pre\- and post\- install scripts, and so on. .sp Different strategies can be used, depending on the system’s resources. I am listing some of them. .SS Double copy with fall\-back .sp If there is enough space on the storage to save two copies of the whole software, it is possible to guarantee that there is always a working copy even if the software update is interrupted or a power off occurs. .sp Each copy must contain the kernel, the root file system, and each further component that can be updated. It is required a mechanism to identify which version is running. .sp SWUpdate should be inserted in the application software, and the application software will trigger it when an update is required. The duty of SWUpdate is to update the stand\-by copy, leaving the running copy of the software untouched. .sp A synergy with the boot loader is often necessary, because the boot loader must decide which copy should be started. Again, it must be possible to switch between the two copies. After a reboot, the boot loader decides which copy should run. [image] .sp Check the chapter about boot loader to see which mechanisms can be implemented to guarantee that the target is not broken after an update. .sp The most evident drawback is the amount of required space. The available space for each copy is less than half the size of the storage. However, an update is always safe even in case of power off. .sp This project supports this strategy. The application as part of this project should be installed in the root file system and started or triggered as required. There is no need of an own kernel, because the two copies guarantees that it is always possible to upgrade the not running copy. .sp SWUpdate will set bootloader’s variable to signal the that a new image is successfully installed. .SS Single copy \- running as standalone image .sp The software upgrade application consists of kernel (maybe reduced dropping not required drivers) and a small root file system, with the application and its libraries. The whole size is much less than a single copy of the system software. Depending on set up, I get sizes from 2.5 until 8 MB for the stand\-alone root file system. If the size is very important on small systems, it becomes negligible on systems with a lot of storage or big NANDs. .sp The system can be put in “upgrade” mode, simply signaling to the boot loader that the upgrading software must be started. The way can differ, for example setting a boot loader environment or using and external GPIO. .sp The boot loader starts “SWUpdate”, booting the SWUpdate kernel and the initrd image as root file system. Because it runs in RAM, it is possible to upgrade the whole storage. Differently as in the double\-copy strategy, the systems must reboot to put itself in update mode. .sp This concept consumes less space in storage as having two copies, but it does not guarantee a fall\-back without updating again the software. However, it can be guaranteed that the system goes automatically in upgrade mode when the productivity software is not found or corrupted, as well as when the upgrade process is interrupted for some reason. [image] .sp In fact, it is possible to consider the upgrade procedure as a transaction, and only after the successful upgrade the new software is set as “boot\-able”. With these considerations, an upgrade with this strategy is safe: it is always guaranteed that the system boots and it is ready to get a new software, if the old one is corrupted or cannot run. With U\-Boot as boot loader, SWUpdate is able to manage U\-Boot’s environment setting variables to indicate the start and the end of a transaction and that the storage contains a valid software. A similar feature for GRUB environment block modification as well as for EFI Boot Guard has been introduced. .sp SWUpdate is mainly used in this configuration. The recipes for Yocto generate an initrd image containing the SWUpdate application, that is automatically started after mounting the root file system. [image] .SS Something went wrong ? .sp Many things can go wrong, and it must be guaranteed that the system is able to run again and maybe able to reload a new software to fix a damaged image. SWUpdate works together with the boot loader to identify the possible causes of failures. Currently U\-Boot, GRUB, and EFI Boot Guard are supported. .sp We can at least group some of the common causes: .INDENT 0.0 .IP \(bu 2 damage / corrupted image during installing. SWUpdate is able to recognize it and the update process is interrupted. The old software is preserved and nothing is really copied into the target’s storage. .IP \(bu 2 corrupted image in the storage (flash) .IP \(bu 2 remote update interrupted due to communication problem. .IP \(bu 2 power\-failure .UNINDENT .sp SWUpdate works as transaction process. The boot loader environment variable “recovery_status” is set to signal the update’s status to the boot loader. Of course, further variables can be added to fine tuning and report error causes. recovery_status can have the values “progress”, “failed”, or it can be unset. .sp When SWUpdate starts, it sets recovery_status to “progress”. After an update is finished with success, the variable is erased. If the update ends with an error, recovery_status has the value “failed”. .sp When an update is interrupted, independently from the cause, the boot loader recognizes it because the recovery_status variable is in “progress” or “failed”. The boot loader can then start again SWUpdate to load again the software (single\-copy case) or run the old copy of the application (double\-copy case). .SS Power Failure .sp If a power off occurs, it must be guaranteed that the system is able to work again \- starting again SWUpdate or restoring an old copy of the software. .sp Generally, the behavior can be split according to the chosen scenario: .INDENT 0.0 .IP \(bu 2 single copy: SWUpdate is interrupted and the update transaction did not end with a success. The boot loader is able to start SWUpdate again, having the possibility to update the software again. .IP \(bu 2 double copy: SWUpdate did not switch between stand\-by and current copy. The same version of software, that was not touched by the update, is started again. .UNINDENT .sp To be completely safe, SWUpdate and the bootloader need to exchange some information. The bootloader must detect if an update was interrupted due to a power\-off, and restart SWUpdate until an update is successful. SWUpdate supports the U\-Boot, GRUB, and EFI Boot Guard bootloaders. U\-Boot and EFI Boot Guard have a power\-safe environment which SWUpdate is able to read and change in order to communicate with them. In case of GRUB, a fixed 1024\-byte environment block file is used instead. SWUpdate sets a variable as flag when it starts to update the system and resets the same variable after completion. The bootloader can read this flag to check if an update was running before a power\-off. [image] .SS What about upgrading SWUpdate itself ? .sp SWUpdate is thought to be used in the whole development process, replacing customized process to update the software during the development. Before going into production, SWUpdate is well tested for a project. .sp If SWUpdate itself should be updated, the update cannot be safe if there is only one copy of SWUpdate in the storage. Safe update can be guaranteed only if SWUpdate is duplicated. .sp There are some ways to circumvent this issue if SWUpdate is part of the upgraded image: .INDENT 0.0 .IP \(bu 2 have two copies of SWUpdate .IP \(bu 2 take the risk, but have a rescue procedure using the boot loader. .UNINDENT .SS What about upgrading the Boot loader ? .sp Updating the boot loader is in most cases a one\-way process. On most SOCs, there is no possibility to have multiple copies of the boot loader, and when boot loader is broken, the board does not simply boot. .sp Some SOCs allow one to have multiple copies of the boot loader. But again, there is no general solution for this because it is \fIvery\fP hardware specific. .sp In my experience, most targets do not allow one to update the boot loader. It is very uncommon that the boot loader must be updated when the product is ready for production. .sp It is different if the U\-Boot environment must be updated, that is a common practice. U\-Boot provides a double copy of the whole environment, and updating the environment from SWUpdate is power\-off safe. Other boot loaders can or cannot have this feature. .SH LICENSE .sp SWUpdate is Free Software. It is copyrighted by Stefano Babic and many others who contributed code (see the actual source code and the git commit messages for details). You can redistribute SWUpdate and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Some files can also be distributed, at your option, under any later version of the GNU General Public License – see individual files for exceptions. .sp To make this easier, license headers in the source files will be replaced with a single line reference to Unique License Identifiers as defined by the Linux Foundation’s SPDX project [1]. For example, in a source file the full “GPL v2.0 only” header text will be replaced by a single line: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SPDX\-License\-Identifier: GPL\-2.0\-only .ft P .fi .UNINDENT .UNINDENT .sp Ideally, the license terms of all files in the source tree should be defined by such License Identifiers; in no case a file can contain more than one such License Identifier list. .sp If a “SPDX\-License\-Identifier:” line references more than one Unique License Identifier, then this means that the respective file can be used under the terms of either of these licenses, i. e. with .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SPDX\-License\-Identifier: GPL\-2.0\-only OR BSD\-3\-Clause .ft P .fi .UNINDENT .UNINDENT .sp you can choose between GPL\-2.0\-only and BSD\-3\-Clause licensing. .sp We use the \fI\%SPDX\fP Unique License Identifiers (\fI\%SPDX\-Identifiers\fP) .SH LICENSES .TS center; |l|l|l|. _ T{ Full name T} T{ SPDX Identifier T} T{ OSI Approved T} _ T{ GNU General Public License \fI\%v2.0\fP only T} T{ GPL\-2.0\-only T} T{ Y T} _ T{ GNU Lesser General Public License \fI\%v2.1\fP or later T} T{ LGPL\-2.1\-or\-later T} T{ Y T} _ T{ BSD \fI\%1\-Clause\fP License T} T{ BSD\-1\-Clause T} T{ Y T} _ T{ BSD \fI\%2\-Clause\fP License T} T{ BSD\-2\-Clause T} T{ Y T} _ T{ BSD \fI\%3\-Clause\fP “New” or “Revised” License T} T{ BSD\-3\-Clause T} T{ Y T} _ T{ \fI\%MIT\fP License T} T{ MIT T} T{ Y T} _ T{ Creative Commons Zero 1.0 Universal (\fI\%CC0\fP) T} T{ CC0\-1.0 T} T{ N T} _ T{ Creative Commons Attribution Share Alike \fI\%4.0\fP T} T{ CC\-BY\-SA\-4.0 T} T{ Y T} _ T{ ISC License (\fI\%ISC\fP) T} T{ ISC T} T{ Y T} _ .TE .SH SWUPDATE: SOFTWARE UPDATE FOR EMBEDDED SYSTEM .SS Overview .sp This project is thought to help to update an embedded system from a storage media or from network. However, it should be mainly considered as a framework, where further protocols or installers (in SWUpdate they are called handlers) can be easily added to the application. .sp One use case is to update from an external local media, as USB\-Pen or SD\-Card. In this case, the update is done without any intervention by an operator: it is thought as “one\-key\-update”, and the software is started at reset simply pressing a key (or in any way that can be recognized by the target), making all checks automatically. At the end, the updating process reports only the status to the operator (successful or failed). .sp The output can be displayed on a LCD using the frame\-buffer device or directed to a serial line (Linux console). .sp It is generally used in the single copy approach, running in an initrd (recipes are provided to generate with Yocto). However, it is possible to use it in a double\-copy approach by use of \fI\%Software collections\fP\&. .sp If started for a remote update, SWUpdate starts an embedded Web\-server and waits for requests. The operator must upload a suitable image, that SWUpdate checks and then install. All output is notified to the operator’s browser via AJAX notifications. .SS Features .SS General Overview .INDENT 0.0 .IP \(bu 2 Install on embedded Media (eMMC, SD, Raw NAND, NOR and SPI\-NOR flashes) .IP \(bu 2 check if an image is available. The image is built in a specified format (cpio) and it must contain a file describing the software that must be updated. .IP \(bu 2 SWUpdate is thought to update UBI volumes (mainly for NAND, but not only) and images on devices. Passing a whole image can still be updated as a partition on the SD card, or a MTD partition. .IP \(bu 2 new partition schema. This is bound with UBI volume. SWUpdate can recreate UBI volumes, resizing them and copying the new software. .IP \(bu 2 support for compressed images, using the zlib and zstd library. tarball (tgz file) are supported. .IP \(bu 2 support for partitioned USB\-pen or unpartitioned (mainly used by Windows). .IP \(bu 2 support for updating a single file inside a filesystem. The filesystem where to put the file must be described. .IP \(bu 2 checksum for the single components of an image .IP \(bu 2 use a structured language to describe the image. This is done using the \fI\%libconfig\fP library as default parser, that uses a JSON\-like description. .IP \(bu 2 use custom’s choice for the description of the image. It is possible to write an own parser using the Lua language. An example using a XML description in Lua is provided in the examples directory. .IP \(bu 2 Support for setting / erasing U\-Boot variables .IP \(bu 2 Support for setting / erasing \fI\%GRUB\fP environment block variables .IP \(bu 2 Support for setting / erasing \fI\%EFI Boot Guard\fP variables .IP \(bu 2 Support for pre and post update commands run before the update starts processing data and after the update has finished successfully. .IP \(bu 2 Support for lua hooks, executed before any handler runs. .IP \(bu 2 Support for preinstall scripts. They run after streamed handlers have handled their data, and before regular handlers. .IP \(bu 2 Support for postinstall scripts. They run after updating the images. .IP \(bu 2 Network installer using an embedded Web\-server (Mongoose Server was chosen, in the version under Lua license). A different Web\-server can be used. .IP \(bu 2 .INDENT 2.0 .TP .B Multiple interfaces for getting software .INDENT 7.0 .IP \(bu 2 local Storage: USB, SD, UART,.. .UNINDENT .UNINDENT .IP \(bu 2 .INDENT 2.0 .TP .B OTA / Remote .INDENT 7.0 .IP \(bu 2 integrated Web\-Server .IP \(bu 2 pulling from remote Server (HTTP, HTTPS, ..) .IP \(bu 2 using a Backend. SWUpdate is open to talk with back end servers for rolling out software updates. Current version supports the hawkBit server, but other backend can be added. .UNINDENT .UNINDENT .IP \(bu 2 Can be configured to check for compatibility between software and hardware revisions. The software image must contain an entry declaring on which HW revision the software is allowed to run. SWUpdate refuses to install if the compatibility is not verified. .IP \(bu 2 support for image extraction. A manufacturer can require to have a single image that contains the software for more as one device. This simplifies the manufacturer’s management and reduces their administrative costs having a single software product. SWUpdate receives the software as stream without temporary storing, and extracts only the required components for the device to be installed. .IP \(bu 2 allow custom handlers for installing FPGA firmware, micro\-controller firmware via custom protocols. .IP \(bu 2 Features are enabled / disabled using “make menuconfig”. (Kbuild is inherited from busybox project) .IP \(bu 2 Images are authenticated and verified before installing .IP \(bu 2 Power\-Off safe .UNINDENT .SS Single image delivery .sp The main concept is that the manufacturer delivers a single big image. All single images are packed together (cpio was chosen for its simplicity and because can be streamed) together with an additional file (sw\-description), that contains meta information about each single image. .sp The format of sw\-description can be customized: SWUpdate can be configured to use its internal parser (based on libconfig), or calling an external parser in Lua. [image] .sp Changing the rules to accept images with an external parser, let to extend to new image types and how they are installed. In fact, the scope of the parser is to retrieve which single images must be installed and how. SWUpdate implements “handlers” to install a single image: there are handlers to install images into UBI volumes, or to a SD card, a CFI Flash, and so on. It is then easy to add an own handler if a very special installer is required. .sp For example we can think at a project with a main processor and one or several micro\-controllers. Let’s say for simplicity that the main processor communicates with the micro\-controllers via UARTS using a proprietary protocol. The software on the micro\-controllers can be updated using the proprietary protocol. .sp It is possible to extend SWUpdate writing a handler, that implements the part of the proprietary protocol to perform the upgrade on the micro\-controller. The parser must recognize which image must be installed with the new handler, and SWUpdate will call the handler during the installation process. .SS Streaming feature .sp SWUpdate is thought to be able to stream the received image directly into the target, without any temporary copy. In fact, the single installer (handler) receive as input the file descriptor set at the beginning of the image that must be installed. .sp The feature can be set on image basis, that means that a user can decide which partial images should be streamed. If not streamed (see installed\-directly flag), files are temporary extracted into the directory pointed to by the environment variable \fBTMPDIR\fP with \fB/tmp\fP as fall\-back if \fBTMPDIR\fP is not set. Of course, by streaming it is not possible to make checks on the whole delivered software before installing. The temporary copy is done only when updated from network. When the image is stored on an external storage, there is no need of that copy. .SS Images fully streamed .sp In case of remote update, SWUpdate extracts relevant images from the stream and copies them into the directory pointed to by the environment variable \fBTMPDIR\fP (if unset, to \fB/tmp\fP) before calling the handlers. This guarantee that an update is initiated only if all parts are present and correct. However, on some systems with less resources, the amount of RAM to copy the images could be not enough, for example if the filesystem on an attached SD Card must be updated. In this case, it will help if the images are installed directly as stream by the corresponding handler, without temporary copies. Not all handlers support to stream directly into the target. Streaming with zero\-copy is enabled by setting the flag “installed\-directly” in the description of the single image. .SS Configuration and build .SS Requirements .sp There are only a few libraries that are required to compile SWUpdate. .INDENT 0.0 .IP \(bu 2 mtd\-utils: internally, mtd\-utils generates libmtd and libubi. They are commonly not exported and not installed, but they are linked by SWUpdate to reuse the same functions for upgrading MTD and UBI volumes. .IP \(bu 2 openssl / wolfssl / mbedtls (optional) for cryptographic operations .IP \(bu 2 p11\-kit & wolfssl (optional) for PKCS#11 support .IP \(bu 2 Lua: liblua and the development headers. .IP \(bu 2 libz is always linked. .IP \(bu 2 libconfig (optional) for the default parser .IP \(bu 2 libarchive (optional) for archive handler .IP \(bu 2 librsync (optional) for support to apply rdiff patches .IP \(bu 2 libjson (optional) for JSON parser and hawkBit .IP \(bu 2 libubootenv (optional) if support for U\-Boot is enabled .IP \(bu 2 libebgenv (optional) if support for EFI Boot Guard is enabled .IP \(bu 2 libcurl used to communicate with network .UNINDENT .sp New handlers can add some other libraries to the requirement list \- check if you need all handlers in case you get build errors, and drop what you do not need. .SS Building with Yocto .sp See corresponding chapter how to build in Yocto. .SS Configuring SWUpdate .sp SWUpdate is configurable via “make menuconfig”. The small footprint is reached using the internal parser and disabling the web server. Any option has a small help describing its usage. In the default configuration, many options are already activated. .sp To configure the options: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C make menuconfig .ft P .fi .UNINDENT .UNINDENT .SS Building .INDENT 0.0 .IP \(bu 2 to cross\-compile, set the CC and CXX variables before running make. It is also possible to set the cross\-compiler prefix as option with make menuconfig. .IP \(bu 2 generate the code .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C make .ft P .fi .UNINDENT .UNINDENT .sp The result is the binary “swupdate”. A second binary “progress” is built, but it is not strictly required. It is an example how to build your own interface to SWUpdate to show a progress bar or whatever you want on your HMI. The example simply prints on the console the current status of the update. .sp In the Yocto buildsystem,: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bitbake swupdate .ft P .fi .UNINDENT .UNINDENT .sp This will build the package .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bitbake swupdate\-image .ft P .fi .UNINDENT .UNINDENT .sp This builds a rescue image. The result is a Ramdisk that can be loaded directly by the bootloader. To use SWUpdate in the double\-copy mode, put the package swupdate into your rootfs. Check your image recipe, and simply add it to the list of the installed packages. .sp For example, if we want to add it to the standard “core\-image\-full\-cmdline” image, we can add a \fIrecipes\-extended/images/core\-image\-full\-cmdline.bbappend\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C IMAGE_INSTALL += \(dq \e swupdate \e swupdate\-www \e \(dq .ft P .fi .UNINDENT .UNINDENT .sp swupdate\-www is the package with the website, that you can customize with your own logo, template ans style. .SS Building a debian package .sp SWUpdate is thought for Embedded Systems and building in an embedded distribution is the first use case. But apart the most used buildsystems for embedded as Yocto or Buildroot, in some cases a standard Linux distro is used. Not only, a distro package allows one to run SWUpdate on Linux PC for test purposes without having to fight with dependencies. Using the debhelper tools, it is possible to generate a debian package. .SS Steps for building a debian package .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&./debian/rules clean \&./debian/rules build fakeroot debian/rules binary .ft P .fi .UNINDENT .UNINDENT .sp The result is a “deb” package stored in the parent directory. .SS Alternative way signing source package .sp You can use dpkg\-buildpackage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C dpkg\-buildpackage \-us \-uc debsign \-k .ft P .fi .UNINDENT .UNINDENT .SS Running SWUpdate .SS What is expected from a SWUpdate run .sp The whole update process can be seen as a set of pipelines. The incoming stream (the SWU file) is processed by each pipe and passed to the next step. First, the SWU is streamed from one of the interfaces : local (USB, filesystem), Webserver, suricatta (one of the backend), etc. The incoming SWU is forwarded to the installer to be examined and installed. A run of SWUpdate consists mainly of the following steps: .INDENT 0.0 .IP \(bu 2 extracts sw\-description from the stream and verifies it It parses sw\-description creating a raw description in RAM about the activities that must be performed. .IP \(bu 2 if Signed Images is activated, extracts sw\-description.sig and validate sw\-description. .IP \(bu 2 check for hardware\-software compatibility, if any, reading hardware revision from hardware and matching with the table in sw\-description. .IP \(bu 2 Parse sw\-description to determine which artefacts in the incoming SWU are required. Not required artifacts are simply skipped. If an “embedded\-script” is defined, it is executed at this point before parsing files. If “hooks” are defined, they are executed as each file is parsed, even if they will be skipped. At the end of the parsing, SWUpdate builds an internal mapping for each artifact to recognize which handler should be called for each of them. .IP \(bu 2 runs the pre update command, if set .IP \(bu 2 runs partition handlers, if required. .IP \(bu 2 .INDENT 2.0 .TP .B reads through the cpio archive one file at a time and either: .INDENT 7.0 .IP \(bu 2 execute handlers for each file marked as “installed\-directly”. checksum is checked while the data is streamed to handler, and copy will be marked as having failed if checksum was not correct failing the rest of the install. .IP \(bu 2 copy other files to a temporary location while checking checksums, stopping if there was a mismatch. .UNINDENT .UNINDENT .IP \(bu 2 iterates through all \fIscripts\fP and call the corresponding handler for pre\-install scripts. Please note: if artifacts are streamed, they will be extracted before this runs. If earlier execution is required, please use the “embedded\-script” or hooks features to ensure code is run before installation takes place. .IP \(bu 2 iterates through all \fIimages\fP and call the corresponding handler for installing on target. .IP \(bu 2 iterates through all \fIfiles\fP and call the corresponding handler for installing on target. .IP \(bu 2 iterates through all \fIscripts\fP and call the corresponding handler for post\-install scripts .IP \(bu 2 iterates through all \fIbootenv\fP and updates the bootloader environment. .IP \(bu 2 reports the status to the operator through the notification interface (logging, traces) and through the progress interface. .IP \(bu 2 runs the post update command, if set. .UNINDENT .sp The first step that fails, stops the entire procedure and an error is reported. .sp To start SWUpdate expecting the image from a file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate \-i .ft P .fi .UNINDENT .UNINDENT .sp To start with the embedded web server: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate \-w \(dq\(dq .ft P .fi .UNINDENT .UNINDENT .sp The main important parameters for the web server are “document\-root” and “port”. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate \-w \(dq\-\-document\-root ./www \-\-port 8080\(dq .ft P .fi .UNINDENT .UNINDENT .sp The embedded web server is taken from the Mongoose project. .sp The list of available options (depending on activated features) is shown with: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate \-h .ft P .fi .UNINDENT .UNINDENT .sp This uses as website the pages delivered with the code. Of course, they can be customized and replaced. The website uses AJAX to communicate with SWUpdate, and to show the progress of the update to the operator. .sp The default port of the Web\-server is 8080. You can then connect to the target with: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C http://:8080 .ft P .fi .UNINDENT .UNINDENT .sp If it works, the start page should be displayed as in next figure. [image] .sp If a correct image is downloaded, SWUpdate starts to process the received image. All notifications are sent back to the browser. SWUpdate provides a mechanism to send to a receiver the progress of the installation. In fact, SWUpdate takes a list of objects that registers itself with the application and they will be informed any time the application calls the notify() function. This allows also for self\-written handlers to inform the upper layers about error conditions or simply return the status. It is then simply to add own receivers to implement customized way to display the results: displaying on a LCD (if the target has one), or sending back to another device via network. An example of the notifications sent back to the browser is in the next figure: [image] .sp Software collections can be specified by passing \fI–select\fP command line option. Assuming \fIsw\-description\fP file contains a collection named \fIstable\fP, with \fIalt\fP installation location, \fISWUpdate\fP can be called like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate \-\-select stable,alt .ft P .fi .UNINDENT .UNINDENT .SS Command line parameters .TS center; |l|l|l|. _ T{ Parameter T} T{ Type T} T{ Description T} _ T{ \-f T} T{ string T} T{ SWUpdate configuration file to use. See \fBexamples/configuration/swupdate.cfg\fP in the source code for details. T} _ T{ \-b T} T{ string T} T{ Available if CONFIG_UBIATTACH is set. It allows one to blacklist MTDs when SWUpdate searches for UBI volumes. Example: U\-Boot and environment in MTD0\-1: \fBswupdate \-b \(dq0 1\(dq\fP\&. T} _ T{ \-B T} T{ string T} T{ Override the default bootloader interface to use \fBloader\fP instead. T} _ T{ \-e T} T{ string T} T{ \fBsel\fP is in the format ,. It allows one to find a subset of rules in the sw\-description file. With it, multiple rules are allowed. One common usage is in case of the dual copy approach. Example: \-e “stable, copy1” ==> install on copy1 \-e “stable, copy2” ==> install on copy2 T} _ T{ .INDENT 0.0 .TP .B \-\-excluded .UNINDENT T} T{ string T} T{ \fBsel\fP is in the format ,. It sets a blacklist of selections that cannot be used for an update. Selections can be activated not only with \-e, but also via IPC. Multiple –excluded are allowed T} _ T{ \-h T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Run usage with help. T} _ T{ \-k T} T{ string T} T{ Available if CONFIG_SIGNED is set. Filename with the public key. T} _ T{ \-K T} T{ string T} T{ Available on CONFIG_ENCRYPTED_IMAGES set. Filename with the symmetric key to be used for decryption. T} _ T{ –cert\-purpose T} T{ string T} T{ Available if CONFIG_SIGNED_IMAGES is set. Set expected certificate purpose. T} _ T{ –forced\-signer\-name T} T{ string T} T{ Available if CONFIG_SIGNED_IMAGES is set. Set expected common name of signer certificate. T} _ T{ –ca\-path T} T{ string T} T{ Available if CONFIG_SIGNED_IMAGES is set. Path to the Certificate Authority (PEM). T} _ T{ –get\-root T} T{ T} T{ Detect and print the root device and exit T} _ T{ \-l T} T{ int T} T{ Set loglevel. T} _ T{ \-L T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Send LOG output to syslog (local). T} _ T{ \-i T} T{ string T} T{ Run SWUpdate with a local .swu file. T} _ T{ \-n T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Run SWUpdate in dry\-run mode. T} _ T{ \-N T} T{ string T} T{ The minimum required version of software. This will be checked with the version of new software and forbids downgrading. Version consists of either 4 numbers (major.minor.rev.build with each field in the range 0..65535) or it is a semantic version. T} _ T{ .INDENT 0.0 .TP .BI \-m\fB ax\-version .UNINDENT T} T{ string T} T{ The maximum required version of software. This will be checked with the version of new software. Version consists of either 4 numbers (major.minor.rev.build with each field in the range 0..65535) or it is a semantic version. T} _ T{ \-R T} T{ string T} T{ The current installed version of software. This will be checked with the version of new software and forbids reinstalling. T} _ T{ \-o T} T{ string T} T{ Save the stream (SWU) to a file. T} _ T{ \-v T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Activate verbose output. T} _ T{ \-M T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Disable setting the bootloader transaction marker. T} _ T{ \-m T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Disable setting the update state in the bootloader. T} _ T{ \-w T} T{ string T} T{ Available if CONFIG_WEBSERVER is set. Start internal webserver and pass to it a command line string. T} _ T{ \-d T} T{ string T} T{ Available if CONFIG_DOWNLOAD is set. Start internal downloader client and pass to it a command line string. See below the internal command line arguments for the downloader. T} _ T{ \-u T} T{ string T} T{ Available if CONFIG_SURICATTA is set. Start internal suricatta client daemon and pass to it a command line string. See below the internal command line arguments for suricatta. T} _ T{ \-H T} T{ string T} T{ Available on CONFIG_HW_COMPATIBILITY set. Set board name and hardware revision. T} _ T{ \-c T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Check \fB*.swu\fP file. It ensures that files referenced in sw\-description are present. Usage: \fBswupdate \-c \-i \fP T} _ T{ \-P T} T{ string T} T{ Execute pre\-update command. T} _ T{ \-p T} T{ string T} T{ Execute post\-update command. T} _ .TE .SS Downloader command line parameters .sp Example: \fBswupdate \-d \(dq\-u example.com\(dq\fP .sp Mandatory arguments are marked with ‘*’: .sp +————— +———\-+——————————————–+ | Parameter | Type | Description | +=============== +==========+============================================+ | \-u | string | * This is the URL where new software is | | | | pulled. URL is a link to a valid .swu image| +————— +———\-+——————————————–+ | \-r | integer | Number of retries before a download is | | | | considered broken. With “\-r 0”, SWUpdate | | | | will not stop until a valid software is | | | | loaded. | +————— +———\-+——————————————–+ | \-w | integer | Time to wait prior to retry and resume a | | | | download (default: 5s). | +————— +———\-+——————————————–+ | \-t | integer | Timeout for connection lost | | | | downloader or Webserver | +————— +———\-+——————————————–+ | \-a | string | Send user and password for Basic Auth | +————— +———\-+——————————————–+ .SS Suricatta command line parameters .sp Example: \fBswupdate \-u \(dq\-t default \-u localhost:8080 \-i 1B7\(dq\fP .sp Note that different suricatta modules may have different parameters. The below listed options are for SWUpdate’s hawkBit support. .sp Mandatory arguments are marked with ‘*’: .TS center; |l|l|l|. _ T{ Parameter T} T{ Type T} T{ Description T} _ T{ \-t T} T{ string T} T{ * Set hawkBit tenant ID for this device. T} _ T{ \-u T} T{ string T} T{ * Host and port of the hawkBit instance, e.g., localhost:8080 T} _ T{ \-i T} T{ integer T} T{ * The device ID to communicate to hawkBit. T} _ T{ \-c T} T{ integer T} T{ Confirm update status to server: 1=AGAIN, 2=SUCCESS, 3=FAILED T} _ T{ \-x T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Do not abort on flawed server certificates. T} _ T{ \-p T} T{ integer T} T{ Delay in seconds between two hawkBit poll operations (default: 45s). T} _ T{ \-r T} T{ integer T} T{ Resume and retry interrupted downloads (default: 5 tries). T} _ T{ \-w T} T{ integer T} T{ Time to wait prior to retry and resume a download (default: 5s). T} _ T{ \-y T} T{ string T} T{ Use proxy. Either give proxy URL, else {http,all}_proxy env is tried. T} _ T{ \-k T} T{ string T} T{ Set target token. T} _ T{ \-g T} T{ string T} T{ Set gateway token. T} _ T{ \-f T} T{ string T} T{ Set the network interface to connect to hawkBit. T} _ T{ \-e T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Daemon enabled at startup (default). T} _ T{ \-d T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Daemon disabled at startup. T} _ T{ –disable\-token\-for\-dwl T} T{ .INDENT 0.0 .IP \(bu 2 .UNINDENT T} T{ Do not send authentication header when downloading SWU. T} _ T{ –cache\-file T} T{ string T} T{ This allows one to resume an update after a power cut. If the SWU is saved in a file, SWUpdate can reuse the file and download just the remaining part of the SWU. T} _ T{ \-m T} T{ integer T} T{ Delay in seconds between re\-trying to send initial feedback specified with “\-c” option. Default value is 10 seconds. If Suricatta is started with initial state of STATE_WAIT (“\-c 6”), this value is ignored. T} _ T{ \-s T} T{ integer T} T{ Connection timeout to use in seconds. If user doesn’t set this option, default libcurl connection timeout value of 300 seconds is used. NOTE: it is not possible for Suricatta to respond to external program API requests during this period \- adapt this value to your use case! T} _ T{ \-a T} T{ strings T} T{ Custom HTTP header with given name and value to be sent with every HTTP request made. T} _ T{ \-n T} T{ string T} T{ Maximum download speed to be used. Value be specified in kB/s, B/s, MB/s or GB/s. Examples: \-n 100k : Set limit to 100 kB/s. \-n 500 : Set limit to 500 B/s. \-n 2M : Set limit to 1 M/s. \-n 1G : Set limit to 1 G/s. T} _ .TE .SS Webserver command line parameters .sp Example: \fBswupdate \-w \(dq\-r /www \-p 8080\(dq\fP .sp Mandatory arguments are marked with ‘*’: .TS center; |l|l|l|. _ T{ Parameter T} T{ Type T} T{ Description T} _ T{ \-r T} T{ string T} T{ * Path where the web app is stored. T} _ T{ \-p T} T{ integer T} T{ * TCP port to be listened if not set, 8080 is used T} _ T{ \-s T} T{ T} T{ * Enable SSL support. Note: it must be configured with CONFIG_MONGOOSESSL T} _ T{ –ssl\-cert T} T{ string T} T{ Path to the certificate to present to clients T} _ T{ \-K T} T{ string T} T{ Path to key corresponding to ssl certificate T} _ T{ \-t T} T{ integer T} T{ Timeout to consider a connection lost if clients stops to send data. If hit, an update is aborted. Default=0 (unlimited) T} _ T{ –auth\-domain T} T{ string T} T{ Set authentication domain Default: none T} _ T{ .INDENT 0.0 .TP .B \-\-global\-auth\-file .UNINDENT T} T{ string T} T{ Set authentication file if any Default: none T} _ .TE .SS systemd Integration .sp SWUpdate has optional \fI\%systemd\fP support via the compile\-time configuration switch \fBCONFIG_SYSTEMD\fP\&. If enabled, SWUpdate signals systemd about start\-up completion and can make optional use of systemd’s socket\-based activation feature. .sp A sample systemd service unit file \fB/etc/systemd/system/swupdate.service\fP may look like the following starting SWUpdate in suricatta daemon mode: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [Unit] Description=SWUpdate daemon Documentation=https://github.com/sbabic/swupdate Documentation=https://sbabic.github.io/swupdate [Service] Type=notify ExecStart=/usr/bin/swupdate \-u \(aq\-t default \-u http://localhost \-i 25\(aq [Install] WantedBy=multi\-user.target .ft P .fi .UNINDENT .UNINDENT .sp Started via \fBsystemctl start swupdate.service\fP, SWUpdate (re)creates its sockets on startup. For using socket\-based activation, an accompanying systemd socket unit file \fB/etc/systemd/system/swupdate.socket\fP is required: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [Unit] Description=SWUpdate socket listener Documentation=https://github.com/sbabic/swupdate Documentation=https://sbabic.github.io/swupdate [Socket] ListenStream=/tmp/sockinstctrl ListenStream=/tmp/swupdateprog [Install] WantedBy=sockets.target .ft P .fi .UNINDENT .UNINDENT .sp On \fBswupdate.socket\fP being started, systemd creates the socket files and hands them over to SWUpdate when it starts. So, for example, when talking to \fB/tmp/swupdateprog\fP, systemd starts \fBswupdate.service\fP and hands\-over the socket files. The socket files are also handed over on a “regular” start of SWUpdate via \fBsystemctl start swupdate.service\fP\&. .sp Note that the socket paths in the two \fBListenStream=\fP directives have to match the socket paths \fBCONFIG_SOCKET_CTRL_PATH\fP and \fBCONFIG_SOCKET_PROGRESS_PATH\fP in SWUpdate’s configuration. Here, the default socket path configuration is depicted. .SS Changes in boot\-loader code .sp The SWUpdate consists of kernel and a root filesystem (image) that must be started by the boot\-loader. In case using U\-Boot, the following mechanism can be implemented: .INDENT 0.0 .IP \(bu 2 U\-Boot checks if a sw update is required (check gpio, serial console, etc.). .IP \(bu 2 the script “altbootcmd” sets the rules to start SWUpdate .IP \(bu 2 in case SWUpdate is required, U\-boot run the script “altbootcmd” .UNINDENT .sp Is it safe to change U\-Boot environment ? Well, it is, but U\-Boot must be configured correctly. U\-Boot supports two copies of the environment to be power\-off safe during an environment update. The board’s configuration file must have defined CONFIG_ENV_OFFSET_REDUND or CONFIG_ENV_ADDR_REDUND. Check in U\-Boot documentation for these constants and how to use them. .sp There are a further enhancement that can be optionally integrated into U\-boot to make the system safer. The most important I will suggest is to add support for boot counter in U\-boot (documentation is in U\-Boot docs). This allows U\-Boot to track for attempts to successfully run the application, and if the boot counter is greater as a limit, can start automatically SWUpdate to replace a corrupt software. .sp GRUB by default does not support double copies of environment as in case of U\-Boot. This means that there is possibility that environment block get’s corrupted when power\-off occurs during environment update. To minimize the risk, we are not modifying original environment block. Variables are written into temporary file and after successful operation rename instruction is called. .SS Building a single image .sp cpio is used as container for its simplicity. The resulting image is very simple to be built. The file describing the images (“sw\-description”, but the name can be configured) must be the first file in the cpio archive. .sp To produce an image, a script like this can be used: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C CONTAINER_VER=\(dq1.0\(dq PRODUCT_NAME=\(dqmy\-software\(dq FILES=\(dqsw\-description image1.ubifs \e image2.gz.u\-boot uImage.bin myfile sdcard.img\(dq for i in $FILES;do echo $i;done | cpio \-ov \-H crc > ${PRODUCT_NAME}_${CONTAINER_VER}.swu .ft P .fi .UNINDENT .UNINDENT .sp The single images can be put in any order inside the cpio container, with the exception of sw\-description, that must be the first one. To check your generated image you can run the following command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate \-c \-i my\-software_1.0.swu .ft P .fi .UNINDENT .UNINDENT .SS Support of compound image .sp The single image can be built automatically inside Yocto. meta\-swupdate extends the classes with the swupdate class. A recipe should inherit it, and add your own sw\-description file to generate the image. .SH UPDATE STRATEGY EXAMPLES .sp SWUpdate is a building block and it allows one to design and implementing its own update strategy. Even if many projects have common ways for updating, it is possible to high customize the update for each project. The most common strategies (single\-copy and dual\-copy) were already described at the beginning of this documentation and of course are well supported in SWUpdate. .SS Single copy \- running as standalone image .sp See \fI\%Single copy \- running as standalone image\fP\&. .SS Double copy with fall\-back .sp See \fI\%Double copy with fall\-back\fP\&. .SS Combine double\-copy with rescue system .sp This provides a recovery procedure to cover update failure in severe cases when software is damaged. In case none of the copy can be started, the bootloader will start the rescue system (possibly stored on another storage as the main system) to try to rescue the board. [image] .sp The rescue system can be updated as well during a standard update. .SS Split system update with application update .sp Updating a whole image is quite straightforward, but this means to transfer bigger amount of data if just a few files are updated. It is possible to split theupdate in several smaller parts to reduce the transfer size. This requires a special care to take care of compatibility between system and application, that can be solved with customized Lua scripts in the sw\-description file. SWUpdate supports versioning for each artefact, and anyone can add own rules to verify compatibility between components. [image] .SS Configuration update .sp Thought to update the software, SWUpdate can be used to install configuration data as well. Build system can create configuration SWU with files / data for the configuration of the system. There is no requirements what these SWU should contains \- it is duty of the integrator to build them and make them suitable for his own project. Again, configuration data can be updated as separate process using one of the above scenarios. .SH SWUPDATE: SYNTAX AND TAGS WITH THE DEFAULT PARSER .SS Introduction .sp SWUpdate uses the library “libconfig” as default parser for the image description. However, it is possible to extend SWUpdate and add an own parser, based on a different syntax and language as the one supported by libconfig. In the examples directory there is the code for a parser written in Lua, with the description in XML. .sp Using the default parser, sw\-description follows the syntax rules described in the libconfig manual. Please take a look at \fI\%http://www.hyperrealm.com/libconfig/libconfig_manual.html\fP for an explanation of basic types. The whole description must be contained in the sw\-description file itself: using of the #include directive is not allowed by SWUpdate. .sp The following example explains the implemented tags: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; description = \(dqFirmware update for XXXXX Project\(dq; hardware\-compatibility: [ \(dq1.0\(dq, \(dq1.2\(dq, \(dq1.3\(dq]; /* partitions tag is used to resize UBI partitions */ partitions: ( /* UBI Volumes */ { name = \(dqrootfs\(dq; device = \(dqmtd4\(dq; size = 104896512; /* in bytes */ }, { name = \(dqdata\(dq; device = \(dqmtd5\(dq; size = 50448384; /* in bytes */ } ); images: ( { filename = \(dqrootfs.ubifs\(dq; volume = \(dqrootfs\(dq; }, { filename = \(dqswupdate.ext3.gz.u\-boot\(dq; volume = \(dqfs_recovery\(dq; }, { filename = \(dqsdcard.ext3.gz\(dq; device = \(dq/dev/mmcblk0p1\(dq; compressed = \(dqzlib\(dq; }, { filename = \(dqbootlogo.bmp\(dq; volume = \(dqsplash\(dq; }, { filename = \(dquImage.bin\(dq; volume = \(dqkernel\(dq; }, { filename = \(dqfpga.txt\(dq; type = \(dqfpga\(dq; }, { filename = \(dqbootloader\-env\(dq; type = \(dqbootloader\(dq; } ); files: ( { filename = \(dqREADME\(dq; path = \(dq/README\(dq; device = \(dq/dev/mmcblk0p1\(dq; filesystem = \(dqvfat\(dq } ); scripts: ( { filename = \(dqerase_at_end\(dq; type = \(dqlua\(dq; }, { filename = \(dqdisplay_info\(dq; type = \(dqlua\(dq; } ); bootenv: ( { name = \(dqvram\(dq; value = \(dq4M\(dq; }, { name = \(dqaddfb\(dq; value = \(dqsetenv bootargs ${bootargs} omapfb.vram=1:2M,2:2M,3:2M omapdss.def_disp=lcd\(dq } ); } .ft P .fi .UNINDENT .UNINDENT .sp The first tag is “software”. The whole description is contained in this tag. It is possible to group settings per device by using \fI\%Board specific settings\fP\&. .SS Handling configuration differences .sp The concept can be extended to deliver a single image containing the release for multiple devices. Each device has its own kernel, dtb, and root filesystem, or they can share some parts. .sp Currently this is managed (and already used in a real project) by writing an own parser, that checks which images must be installed after recognizing which is the device where software is running. .sp Because the external parser can be written in Lua and it is completely customizable, everybody can set his own rules. For this specific example, the sw\-description is written in XML format, with tags identifying the images for each device. To run it, the liblxp library is needed. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Update Image 1.0.0 Firmware for XXXXX Project .ft P .fi .UNINDENT .UNINDENT .sp The parser for this is in the /examples directory. By identifying which is the running device, the parser return a table containing the images that must be installed and their associated handlers. By reading the delivered image, SWUpdate will ignore all images that are not in the list processed by the parser. In this way, it is possible to have a single delivered image for the update of multiple devices. .sp Multiple devices are supported by the default parser, too. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; target\-1 = { images: ( { ... } ); }; target\-2 = { images: ( { ... } ); }; } .ft P .fi .UNINDENT .UNINDENT .sp In this way, it is possible to have a single image providing software for each device you have. .sp By default, the hardware information is extracted from \fI/etc/hwrevision\fP file. The file should contain a single line in the following format: .INDENT 0.0 .INDENT 3.5 .UNINDENT .UNINDENT .sp Where: .INDENT 0.0 .IP \(bu 2 \fI\fP will be used for matching with hardware compatibility list .IP \(bu 2 \fI\fP can be used for grouping board specific settings .UNINDENT .SS Software collections .sp Software collections and operation modes can be used to implement a dual copy strategy. The simplest case is to define two installation locations for the firmware image and call \fISWUpdate\fP selecting the appropriate image. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; stable = { copy\-1: { images: ( { device = \(dq/dev/mtd4\(dq ... } ); } copy\-2: { images: ( { device = \(dq/dev/mtd5\(dq ... } ); } }; } .ft P .fi .UNINDENT .UNINDENT .sp In this way it is possible to specify that \fIcopy\-1\fP gets installed to \fI/dev/mtd4\fP, while \fIcopy\-2\fP to \fI/dev/mtd5\fP\&. By properly selecting the installation locations, \fISWUpdate\fP will update the firmware in the other slot. .sp The method of image selection is out of the scope of SWUpdate and user is responsible for calling \fISWUpdate\fP passing proper settings. .SS Priority finding the elements in the file .sp SWUpdate search for entries in the sw\-description file according to the following priority: .INDENT 0.0 .IP 1. 3 Try ... .IP 2. 3 Try .. .IP 3. 3 Try . .IP 4. 3 Try .UNINDENT .sp Take an example. The following sw\-description describes the release for a set of boards. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; myboard = { stable = { copy\-1: { images: ( { device = \(dq/dev/mtd4\(dq ... } ); } copy\-2: { images: ( { device = \(dq/dev/mtd5\(dq ... } ); } } } stable = { copy\-1: { images: ( { device = \(dq/dev/mtd6\(dq ... } ); } copy\-2: { images: ( { device = \(dq/dev/mtd7\(dq ... } ); } } } .ft P .fi .UNINDENT .UNINDENT .sp On \fImyboard\fP, SWUpdate searches and finds myboard.stable.copy1(2). When running on different boards, SWUpdate does not find an entry corresponding to the boardname and it falls back to the version without boardname. This allows to realize the same release for different boards having a completely different hardware. \fImyboard\fP could have an eMMC and an ext4 filesystem, while another device can have raw flash and install an UBI filesystem. Nevertheless, they are both just a different format of the same release and they could be described together in sw\-description. It is important to understand the priorities how SWUpdate scans for entries during the parsing. .SS Using links .sp sw\-description can become very complex. Let’s think to have just one board, but in multiple hw revision and they differ in Hardware. Some of them can be grouped together, some of them require a dedicated section. A way (but not the only one !) could be to add \fImode\fP and selects the section with \fI\-e stable,\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; myboard = { stable = { hardware\-compatibility: [\(dq1.0\(dq, \(dq1.2\(dq, \(dq2.0\(dq, \(dq1.3\(dq, \(dq3.0\(dq, \(dq3.1\(dq]; rev\-1.0: { images: ( ... ); scripts: ( ... ); } rev\-1.2: { hardware\-compatibility: [\(dq1.2\(dq]; images: ( ... ); scripts: ( ... ); } rev\-2.0: { hardware\-compatibility: [\(dq2.0\(dq]; images: ( ... ); scripts: ( ... ); } rev\-1.3: { hardware\-compatibility: [\(dq1.3\(dq]; images: ( ... ); scripts: ( ... ); } rev\-3.0: { hardware\-compatibility: [\(dq3.0\(dq]; images: ( ... ); scripts: ( ... ); } rev\-3.1: { hardware\-compatibility: [\(dq3.1\(dq]; images: ( ... ); scripts: ( ... ); } } } } .ft P .fi .UNINDENT .UNINDENT .sp If each of them requires an own section, it is the way to do. Anyway, it is more probable than revisions can be grouped together, for example board with the same major revision number could have the same installation instructions. This leads in the example to 3 groups for rev1.X, rev2.X, and rev3.X. Links allow one to group section together. When a “ref” is found when SWUpdate searches for a group (images, files, script, bootenv), it replaces the current path in the tree with the value of the string. In this way, the example above can be written in this way: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; myboard = { stable = { hardware\-compatibility: [\(dq1.0\(dq, \(dq1.2\(dq, \(dq2.0\(dq, \(dq1.3\(dq, \(dq3.0\(dq, \(dq3.1\(dq]; rev\-1x: { images: ( ... ); scripts: ( ... ); } rev1.0 = { ref = \(dq#./rev\-1x\(dq; } rev1.2 = { ref = \(dq#./rev\-1x\(dq; } rev1.3 = { ref = \(dq#./rev\-1x\(dq; } rev\-2x: { images: ( ... ); scripts: ( ... ); } rev2.0 = { ref = \(dq#./rev\-2x\(dq; } rev\-3x: { images: ( ... ); scripts: ( ... ); } rev3.0 = { ref = \(dq#./rev\-3x\(dq; } rev3.1 = { ref = \(dq#./rev\-3x\(dq; } } } } .ft P .fi .UNINDENT .UNINDENT .sp The link can be absolute or relative. The keyword \fI“ref”\fP is used to indicate a link. If this is found, SWUpdate will traverse the tree and replaces the current path with the values find in the string pointed by “ref”. There are simple rules for a link: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 it must start with the character ‘#’ .IP \(bu 2 “.” points to the current level in the tree, that means the parent of “ref” .IP \(bu 2 “..” points to the parent level in the tree .IP \(bu 2 “/” is used as filed separator in the link .UNINDENT .UNINDENT .UNINDENT .sp A relative path has a number of leading “../” to move the current cursor to the parent leaf of the tree. In the following example, rev40 sets a link to a “common” section, where \fIimages\fP is found. This is sets via a link, too, to a section in the parent node. The path \fIsoftware.myboard.stable.common.images\fP is then replaced by \fIsoftware.myboard.stable.trythis\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = { ref = \(dq#./commonversion\(dq; } hardware\-compatibility = [\(dqrev10\(dq, \(dqrev11\(dq, \(dqrev20\(dq]; commonversion = \(dq0.7\-linked\(dq; pc:{ stable:{ common:{ images = { ref = \(dq#./../trythis\(dq; } }; trythis:( { filename = \(dqrootfs1.ext4\(dq; device = \(dq/dev/mmcblk0p8\(dq; type = \(dqraw\(dq; } , { filename = \(dqrootfs5.ext4\(dq; device = \(dq/dev/mmcblk0p7\(dq; type = \(dqraw\(dq; } ); pdm3rev10: { images:( { filename = \(dqrootfs.ext3\(dq; device = \(dq/dev/mmcblk0p2\(dq;} ); uboot:( { name = \(dqbootpart\(dq; value = \(dq0:2\(dq;} ); }; pdm3rev11 = { ref = \(dq#./pdm3rev10\(dq; } pdm3rev20 = { ref = \(dq#./pdm3rev10\(dq; } pdm3rev40 = { ref = \(dq#./common\(dq; } }; }; } .ft P .fi .UNINDENT .UNINDENT .sp Each entry in sw\-description can be redirect by a link as in the above example for the “version” attribute. .SS hardware\-compatibility .sp \fBhardware\-compatibility: [ \(dqmajor.minor\(dq, \(dqmajor.minor\(dq, ... ]\fP .sp This entry lists the hardware revisions that are compatible with this software image. .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C hardware\-compatibility: [ \(dq1.0\(dq, \(dq1.2\(dq, \(dq1.3\(dq]; .ft P .fi .UNINDENT .UNINDENT .sp This defines that the software is compatible with HW\-Revisions 1.0, 1.2, and 1.3, but not with 1.1 or any other version not explicitly listed here. In the above example, compatibility is checked by means of string comparison. If the software is compatible with a large number of hardware revisions, it may get cumbersome to enumerate all compatible versions. To allow more compact specifications, regular expressions (POSIX extended) can be used by adding a prefix \fB#RE:\fP to the entry. Rewriting the above example would yield: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C hardware\-compatibility: [ \(dq#RE:^1\e.[023]$\(dq ]; .ft P .fi .UNINDENT .UNINDENT .sp It is in the responsibility of the respective project to find the revision of the board on which SWUpdate is running. No assumptions are made about how the revision can be obtained (GPIOs, EEPROM,..) and each project is free to select the most appropriate way. In the end the result must be written to the file \fB/etc/hwrevision\fP (or in another file if specified as configuration option) before SWUpdate is started. .SS partitions : UBI layout .sp This tag allows one to change the layout of UBI volumes. Please take care that MTDs are not touched and they are configured by the Device Tree or in another way directly in kernel. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C partitions: ( { name = ; size = ; device = ; } ); .ft P .fi .UNINDENT .UNINDENT .sp All fields are mandatory. SWUpdate searches for a volume of the given name and if necessary adjusts size or type (see below). If no volume with the given name is found, a new volume is created on the UBI device attached to the MTD device given by \fBdevice\fP\&. \fBdevice\fP can be specified by number (e.g. “mtd4”) or by name (the name of the MTD device, e.g. “ubi_partition”). The UBI device is attached automatically. .sp The default behavior of swupdate is to create a dynamic UBI volume. To create a static volume, add a line \fBdata = \(dqstatic\(dq;\fP to the respective partition entry. .sp If a size of 0 is given, the volume will be deleted if it exists. This can be used to remove orphan volumes possibly created by older software versions which are not required anymore. .SS images .sp The tag “images” collects the image that are installed to the system. The syntax is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename[mandatory] = ; volume[optional] = ; device[optional] = ; mtdname[optional] = ; type[optional] = ; /* optionally, the image can be copied at a specific offset */ offset[optional] = ; /* optionally, the image can be compressed if it is in raw mode */ compressed; }, /* Next Image */ ..... ); .ft P .fi .UNINDENT .UNINDENT .sp \fIvolume\fP is only used to install the image in a UBI volume. \fIvolume\fP and \fIdevice\fP cannot be used at the same time. If device is set, the raw handler is automatically selected. .sp The following example is to update a UBI volume: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dqcore\-image\-base.ubifs\(dq; volume = \(dqrootfs\(dq; } .ft P .fi .UNINDENT .UNINDENT .sp To update an image in raw mode, the syntax is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dqcore\-image\-base.ext3\(dq; device = \(dq/dev/mmcblk0p1\(dq; } .ft P .fi .UNINDENT .UNINDENT .sp To flash an image at a specific offset, the syntax is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dqu\-boot.bin\(dq; device = \(dq/dev/mmcblk0p1\(dq; offset = \(dq16K\(dq; } .ft P .fi .UNINDENT .UNINDENT .sp The offset handles the following multiplicative suffixes: K=1024 and M=1024*1024. .sp However, writing to flash in raw mode must be managed in a special way. Flashes must be erased before copying, and writing into NAND must take care of bad blocks and ECC errors. For these reasons, the handler “flash” must be selected: .sp For example, to copy the kernel into the MTD7 of a NAND flash: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dquImage\(dq; device = \(dqmtd7\(dq; type = \(dqflash\(dq; } .ft P .fi .UNINDENT .UNINDENT .sp The \fIfilename\fP is mandatory. It is the Name of the file extracted by the stream. \fIvolume\fP is only mandatory in case of UBI volumes. It should be not used in other cases. .sp Alternatively, for the handler “flash”, the \fImtdname\fP can be specified, instead of the device name: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dquImage\(dq; mtdname = \(dqkernel\(dq; type = \(dqflash\(dq; } .ft P .fi .UNINDENT .UNINDENT .SS Files .sp It is possible to copy single files instead of images. This is not the preferred way, but it can be used for debugging or special purposes. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C files: ( { filename = ; path = ; device[optional] = ; filesystem[optional] = ; properties[optional] = {create\-destination = \(dqtrue\(dq;} } ); .ft P .fi .UNINDENT .UNINDENT .sp Entries in “files” section are managed as single files. The attributes “filename” and “path” are mandatory. Attributes “device” and “filesystem” are optional; they tell SWUpdate to mount device (of the given filesystem type, e.g. “ext4”) before copying “filename” to “path”. Without “device” and “filesystem”, the “filename” will be copied to “path” in the current rootfs. .sp As a general rule, swupdate doesn’t copy out a file if the destination path doesn’t exists. This behavior could be changed using the special property “create\-destination”. .sp As another general rule, the raw file handler installs the file directly to the specified path. If the target file already exists and the raw file handler is interrupted, the existing file may be replaced by an empty or partially written file. A use case can exist where having an empty or corrupted file is worse than the existing file. For this reason, the raw file handler supports an “atomic\-install” property. Setting the property to “true” installs the file to the specified path with “.tmp” appended to the filename. Once the contents of the file have been written and the buffer is flushed, the “.tmp” file is renamed to the target file. This minimizes chances that an empty or corrupted file is created by an interrupted raw file handler. .SS Scripts .sp Scripts runs in the order they are put into the sw\-description file. The result of a script is valuated by SWUpdate, that stops the update with an error if the result is <> 0. .sp They are copied into a temporary directory before execution and their name must be unique inside the same cpio archive. .sp If no type is given, SWUpdate default to “lua”. .SS Lua .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { filename = ; type = \(dqlua\(dq; } ); .ft P .fi .UNINDENT .UNINDENT .sp Lua scripts are run using the internal interpreter. .sp They must have at least one of the following functions: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C function preinst() .ft P .fi .UNINDENT .UNINDENT .sp SWUpdate scans for all scripts and check for a preinst function. It is called before installing the images. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C function postinst() .ft P .fi .UNINDENT .UNINDENT .sp SWUpdate scans for all scripts and check for a postinst function. It is called after installing the images. .SS shellscript .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { filename = ; type = \(dqshellscript\(dq; } ); .ft P .fi .UNINDENT .UNINDENT .sp 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. .SS preinstall .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { filename = ; type = \(dqpreinstall\(dq; } ); .ft P .fi .UNINDENT .UNINDENT .sp preinstall are shell scripts and called via system command. SWUpdate scans for all scripts and calls them before installing the images. If the data attribute is defined, its value is passed as the last argument(s) to the script. .SS postinstall .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { filename = ; type = \(dqpostinstall\(dq; } ); .ft P .fi .UNINDENT .UNINDENT .sp postinstall are shell scripts and called via system command. SWUpdate scans for all scripts and calls them after installing the images. If the data attribute is defined, its value is passed as the last argument(s) to the script. .SS Update Transaction and Status Marker .sp By default, SWUpdate sets the bootloader environment variable “recovery_status” to “in_progress” prior to an update operation and either unsets it or sets it to “failed” after the update operation. This is an interface for SWUpdate\-external tooling: If there is no “recovery_status” variable in the bootloader’s environment, the update operation has been successful. Else, if there is a “recovery_status” variable with the value “failed”, the update operation has not been successful. .sp While this is in general essential behavior for firmware updates, it needn’t be for less critical update operations. Hence, whether or not the update transaction marker is set by SWUpdate can be controlled by the boolean switch “bootloader_transaction_marker” which is global per \fIsw\-description\fP file. It defaults to \fBtrue\fP\&. The following example snippet disables the update transaction marker: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; bootloader_transaction_marker = false; ... .ft P .fi .UNINDENT .UNINDENT .sp It is also possible to disable setting of the transaction marker entirely (and independently of the setting in \fIsw\-description\fP) by starting SWUpdate with the \fI\-M\fP option. .sp The same applies to setting the update state in the bootloader via its environment variable “ustate” (default) to \fISTATE_INSTALLED=1\fP or \fISTATE_FAILED=3\fP after an installation. This behavior can be turned off globally via the \fI\-m\fP option to SWUpdate or per \fIsw\-description\fP via the boolean switch “bootloader_state_marker”. .SS bootloader .sp There are two ways to update the bootloader (currently U\-Boot, GRUB, and EFI Boot Guard) environment. First way is to add a file with the list of variables to be changed and setting “bootloader” as type of the image. This informs SWUpdate to call the bootloader handler to manage the file (requires enabling bootloader handler in configuration). There is one bootloader handler for all supported bootloaders. The appropriate bootloader must be chosen from the bootloader selection menu in \fImenuconfig\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dqbootloader\-env\(dq; type = \(dqbootloader\(dq; } ) .ft P .fi .UNINDENT .UNINDENT .sp The format of the file is described in U\-boot documentation. Each line is in the format .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C = .ft P .fi .UNINDENT .UNINDENT .sp if value is missing, the variable is unset. .sp The format is compatible with U\-Boot “env import” command. It is possible to produce the file from target as result of “env export”. .sp Comments are allowed in the file to improve readability, see this example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Default variables bootslot=0 board_name=myboard baudrate=115200 ## Board Revision dependent board_revision=1.0 .ft P .fi .UNINDENT .UNINDENT .sp The second way is to define in a group setting the variables that must be changed: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bootenv: ( { name = ; value = ; } ) .ft P .fi .UNINDENT .UNINDENT .sp SWUpdate will internally generate a script that will be passed to the bootloader handler for adjusting the environment. .sp For backward compatibility with previously built \fI\&.swu\fP images, the “uboot” group name is still supported as an alias. However, its usage is deprecated. .SS Board specific settings .sp Each setting can be placed under a custom tag matching the board name. This mechanism can be used to override particular setting in board specific fashion. .sp Assuming that the hardware information file \fI/etc/hwrevision\fP contains the following entry: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C my\-board 0.1.0 .ft P .fi .UNINDENT .UNINDENT .sp and the following description: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; my\-board = { bootenv: ( { name = \(dqbootpart\(dq; value = \(dq0:2\(dq; } ); }; bootenv: ( { name = \(dqbootpart\(dq; value = \(dq0:1\(dq; } ); } .ft P .fi .UNINDENT .UNINDENT .sp SWUpdate will set \fIbootpart\fP to \fI0:2\fP in bootloader’s environment for this board. For all other boards, \fIbootpart\fP will be set to \fI0:1\fP\&. Board specific settings take precedence over default scoped settings. .SS Software collections and operation modes .sp Software collections and operations modes extend the description file syntax to provide an overlay grouping all previous configuration tags. The mechanism is similar to \fI\%Board specific settings\fP and can be used for implementing a dual copy strategy or delivering both stable and unstable images within a single update file. .sp The mechanism uses a custom user\-defined tags placed within \fIsoftware\fP scope. The tag names must not be any of: \fIversion\fP, \fIhardware\-compatibility\fP, \fIuboot\fP, \fIbootenv\fP, \fIfiles\fP, \fIscripts\fP, \fIpartitions\fP, \fIimages\fP .sp An example description file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1\(dq; hardware\-compatibility = [ \(dqrevA\(dq ]; /* differentiate running image modes/sets */ stable: { main: { images: ( { filename = \(dqrootfs.ext3\(dq; device = \(dq/dev/mmcblk0p2\(dq; } ); bootenv: ( { name = \(dqbootpart\(dq; value = \(dq0:2\(dq; } ); }; alt: { images: ( { filename = \(dqrootfs.ext3\(dq; device = \(dq/dev/mmcblk0p1\(dq; } ); bootenv: ( { name = \(dqbootpart\(dq; value = \(dq0:1\(dq; } ); }; }; } .ft P .fi .UNINDENT .UNINDENT .sp The configuration describes a single software collection named \fIstable\fP\&. Two distinct image locations are specified for this collection: \fI/dev/mmcblk0p1\fP and \fI/dev/mmcblk0p2\fP for \fImain\fP mode and \fIalt\fP mode respectively. .sp This feature can be used to implement a dual copy strategy by specifying the collection and mode explicitly. .SS Versioning schemas in SWUpdate .sp SWUpdate can perform version comparisons for the whole Software by checking the \fIversion\fP attribute in the common part of sw\-description and / or for single artifacts. SWUpdate supports two different version schemas, and they must be followed if version comparison is requested. .SS Numbering schema (default) .sp SWUpdate supports a version based on the schema: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ... .ft P .fi .UNINDENT .UNINDENT .sp where each field is a plain number (no alphanumeric) in the range 0..65535. User can add further fields using the dot separator, but they are not considered for version comparison. SWUpdate will check if a version number is set according to this rule and fall back to semantic version upon failure. The version is converted to a 64 bit number (each field is 16 bit) and compared against the running version of the same artifact. .sp Please consider that, because additional fields are descriptive only, for the comparison they are not considered. This example contains version numbers that are interpreted as the same version number by SWUpdate: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 1.2.3.4 1.2.3.4.5 1.2.3.4.5.6 .ft P .fi .UNINDENT .UNINDENT .sp But the following is different: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 1.2.3.4\-alpha .ft P .fi .UNINDENT .UNINDENT .sp And it is treated as semantic version. .SS Semantic version .sp SWUpdate supports \fI\%semantic\fP version. See official documentation for more details. .SS Checking version of installed software .sp SWUpdate can optionally verify if a sub\-image is already installed and, if the version to be installed is exactly the same, it can skip to install it. This is very useful in case some high risky image should be installed or to speed up the upgrade process. One case is if the bootloader needs to be updated. In most time, there is no need to upgrade the bootloader, but practice showed that there are some cases where an upgrade is strictly required \- the project manager should take the risk. However, it is nicer to have always the bootloader image as part of the .swu file, allowing to get the whole distro for the device in a single file, but the device should install it just when needed. .sp SWUpdate searches for a file (/etc/sw\-versions is the default location) containing all versions of the installed images. This must be generated before running SWUpdate. The file must contain pairs with the name of image and version, as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C .ft P .fi .UNINDENT .UNINDENT .sp In sw\-description, the optional attributes “name”, “version”, and “install\-if\-different” provide the connection. Name and version are then compared with the data in the versions file. install\-if\-different is a boolean that enables the check for this image. It is then possible to check the version just for a subset of the images to be installed. .sp If used with “install\-if\-different”, then version can be any string. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bootloader 2015.01\-rc3\-00456\-gd4978d kernel 3.17.0\-00215\-g2e876af .ft P .fi .UNINDENT .UNINDENT .sp There is also an attribute “install\-if\-higher” that checks if the version of the new software is higher than the version of the installed software. If it’s false, the new software isn’t installed. The goal is to avoid installing an older version of software. .sp In this case, version can be any of 2 formats. Either the version consists of \fIup to\fP 4 numbers in the range 0..65535 separated by a dot, e.g. \fI...\fP, or it is a \fI\%semantic version\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bootloader 2018.03.01 kernel 3.17.0\-pre1+g2e876af rfs 0.17\-foo3.bar5+2020.07.01 app 1.7 .ft P .fi .UNINDENT .UNINDENT .sp It is advised not to mix version formats! Semantic versions only support 3 numbers (major, minor, patch) and the fourth number will be silently dropped if present. .SS Embedded Script .sp It is possible to embed a script inside sw\-description. This is useful in a lot of conditions where some parameters are known just by the target at runtime. The script is global to all sections, but it can contain several functions that can be specific for each entry in the sw\-description file. .sp These attributes are used for an embedded\-script: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C embedded\-script = \(dq .ft P .fi .UNINDENT .UNINDENT .sp It must be taken into account that the parser has already run and usage of double quotes can interfere with the parser. For this reason, each double quote in the script must be escaped. .sp That means a simple Lua code as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C print (\(dqTest\(dq) .ft P .fi .UNINDENT .UNINDENT .sp must be changed to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C print (\e\(dqTest\e\(dq) .ft P .fi .UNINDENT .UNINDENT .sp If not, the parser thinks to have the closure of the script and this generates an error. See the examples directory for examples how to use it. Any entry in files or images can trigger one function in the script. The “hook” attribute tells the parser to load the script and to search for the function pointed to by the hook attribute. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C files: ( { filename = \(dqexamples.tar\(dq; type = \(dqarchive\(dq; path = \(dq/tmp/test\(dq; hook = \(dqset_version\(dq; preserve\-attributes = true; } ); .ft P .fi .UNINDENT .UNINDENT .sp After the entry is parsed, the parser runs the Lua function pointed to by hook. If Lua is not activated, the parser raises an error because a sw\-description with an embedded script must be parsed, but the interpreter is not available. .sp Each Lua function receives as parameter a table with the setup for the current entry. A hook in Lua is in the format: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C function lua_hook(image) .ft P .fi .UNINDENT .UNINDENT .sp image is a table where the keys are the list of available attributes. If an attribute contains a “\-”, it is replaced with “_”, because “\-” cannot be used in Lua. This means, for example, that: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C install\-if\-different ==> install_if_different installed\-directly ==> installed_directly .ft P .fi .UNINDENT .UNINDENT .sp Attributes can be changed in the Lua script and values are taken over on return. The Lua function must return 2 values: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 a boolean, to indicate whether the parsing was correct .IP \(bu 2 the image table or nil to indicate that the image should be skipped .UNINDENT .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C function set_version(image) print (\e\(dqRECOVERY_STATUS.RUN: \e\(dq.. swupdate.RECOVERY_STATUS.RUN) for k,l in pairs(image) do swupdate.trace(\e\(dqimage[\e\(dq .. tostring(k) .. \e\(dq] = \e\(dq .. tostring(l)) end image.version = \e\(dq1.0\e\(dq image.install_if_different = true return true, image end .ft P .fi .UNINDENT .UNINDENT .sp The example sets a version for the installed image. Generally, this is detected at runtime reading from the target. .SS Attribute reference .sp There are 4 main sections inside sw\-description: .INDENT 0.0 .IP \(bu 2 images: entries are images and SWUpdate has no knowledge about them. .IP \(bu 2 files: entries are files, and SWUpdate needs a filesystem for them. This is generally used to expand from a tar\-ball or to update single files. .IP \(bu 2 scripts: all entries are treated as executables, and they will be run twice (as pre\- and post\- install scripts). .IP \(bu 2 bootenv: entries are pair with bootloader environment variable name and its value. .UNINDENT .SS Attributes in sw\-description .TS center; |l|l|l|l|. _ T{ Name T} T{ Type T} T{ Applies to T} T{ Description T} _ T{ filename T} T{ string T} T{ images files scripts T} T{ filename as found in the cpio archive T} _ T{ volume T} T{ string T} T{ images T} T{ Just if type = “ubivol”. UBI volume where image must be installed. T} _ T{ ubipartition T} T{ string T} T{ images T} T{ Just if type = “ubivol”. Volume to be created or adjusted with a new size T} _ T{ device T} T{ string T} T{ images files T} T{ devicenode as found in /dev or a symlink to it. Can be specified as absolute path or a name in /dev folder For example if /dev/mtd\-dtb is a link to /dev/mtd3 “mtd3”, “mtd\-dtb”, “/dev/mtd3” and “/dev/mtd\-dtb” are valid names. Usage depends on handler. For files, it indicates on which device the “filesystem” must be mounted. If not specified, the current rootfs will be used. T} _ T{ filesystem T} T{ string T} T{ files T} T{ indicates the filesystem type where the file must be installed. Only used if “device” attribute is set. T} _ T{ path T} T{ string T} T{ files T} T{ For files: indicates the path (absolute) where the file must be installed. If “device” and “filesystem” are set, SWUpdate will install the file after mounting “device” with “filesystem” type. (path is always relative to the mount point.) T} _ T{ preserve\-attributes T} T{ bool T} T{ files T} T{ flag to control whether the following attributes will be preserved when files are unpacked from an archive (assuming destination filesystem supports them, of course): timestamp, uid/gid (numeric), perms, file attributes, extended attributes T} _ T{ type T} T{ string T} T{ images files scripts T} T{ string identifier for the handler, as it is set by the handler when it registers itself. Example: “ubivol”, “raw”, “rawfile”, T} _ T{ compressed T} T{ string T} T{ images files T} T{ string to indicate the “filename” is compressed and must be decompressed before being installed. the value denotes the compression type. currently supported values are “zlib” and “zstd”. T} _ T{ compressed T} T{ bool (dep recated) T} T{ images files T} T{ Deprecated. Use the string form. true is equal to ‘compressed = “zlib”’. T} _ T{ installed\-directly T} T{ bool T} T{ images T} T{ flag to indicate that image is streamed into the target without any temporary copy. Not all handlers support streaming. T} _ T{ name T} T{ string T} T{ bootenv T} T{ name of the bootloader variable to be set. T} _ T{ value T} T{ string T} T{ bootenv T} T{ value to be assigned to the bootloader variable T} _ T{ name T} T{ string T} T{ images files T} T{ name that identifies the sw\-component it can be any string and it is compared with the entries in sw\-versions T} _ T{ version T} T{ string T} T{ images files T} T{ version for the sw\-component it can be any string and it is compared with the entries in sw\-versions T} _ T{ description T} T{ string T} T{ T} T{ user\-friendly description of the swupdate archive (any string) T} _ T{ install\-if\-different T} T{ bool T} T{ images files T} T{ flag if set, name and version are compared with the entries in sw\-versions T} _ T{ install\-if\-higher T} T{ bool T} T{ images files T} T{ flag if set, name and version are compared with the entries in sw\-versions T} _ T{ encrypted T} T{ bool T} T{ images files scripts T} T{ flag if set, file is encrypted and must be decrypted before installing. T} _ T{ ivt T} T{ string T} T{ images files scripts T} T{ IVT in case of encrypted artefact It has no value if “encrypted” is not set. Each artefact can have an own IVT to avoid attacker can guess the the key. It is an ASCII string of 32 chars T} _ T{ data T} T{ string T} T{ images files scripts T} T{ This is used to pass arbitrary data to a handler. T} _ T{ sha256 T} T{ string T} T{ images files scripts T} T{ sha256 hash of image, file or script. Used for verification of signed images. T} _ T{ embedded\-script T} T{ string T} T{ T} T{ Lua code that is embedded in the sw\-description file. T} _ T{ offset T} T{ string T} T{ images T} T{ Optional destination offset T} _ T{ hook T} T{ string T} T{ images files T} T{ The name of the function (Lua) to be called when the entry is parsed. T} _ T{ mtdname T} T{ string T} T{ images T} T{ name of the MTD to update. Used only by the flash handler to identify the the mtd to update, instead of specifying the devicenode T} _ .TE .SH UPDATE IMAGES FROM VERIFIED SOURCE .sp It is becoming very important that a device must not only be safely updated, but also that it can verify if the delivered image is coming from a known source and it was not corrupted introducing some malware. .sp To achieve this goal, SWUpdate must verify the incoming images. There are several ways to do this. Should the compound image be signed ? Or some parts of it ? .sp Advantages and disadvantages are described in the following chapter. .SS Signing the compound image .sp It looks quite straightforward if the whole compound image is signed. However, this has some heavy drawbacks. It is not possible to know if the image is verified until the whole image is loaded. This means that verification can be done after installing the single images instead of doing it before touching the device. This leads to have some uninstall procedure if part of a not verified image is already installed, procedures that cannot be safe in case of power off letting some unwanted piece of software on the device. .SS Signing the sub\-images .sp If each sub\-image is signed, the verification is done before calling the corresponding hardware. Only signed images can be installed. Anyway, this remains unbound with the description of the release in sw\-description. Even if sw\-description is signed, an attacker can mix signed images together generating a new compound image that can be installed as well, because all sub\-images are verified. .SS Combining signing sw\-description with hash verification .sp To avoid the described drawbacks, SWUpdate combines signed sw\-description with the verification of hashes for each single image. This means that only sw\-description generated by a verified source can be accepted by the installer. sw\-description contains hashes for each sub\-image to verify that each delivered sub\-image really belongs to the release. .SS Choice of algorithm .sp The algorithm chosen to sign and verify the sw\-descrription file can be selected via menuconfig. Currently, the following mechanisms are implemented: .INDENT 0.0 .IP \(bu 2 RSA Public / private key. The private key belongs to the build system, while the public key must be installed on the target. .IP \(bu 2 CMS using certificates .UNINDENT .sp Key or certificate is passed to SWUpdate with the \fI\-k\fP parameter. .SS Tool to generate keys / certificates .sp The \fIopenssl\fP tool is used to generate the keys. This is part of the OpenSSL project. A complete documentation can be found at the \fI\%openSSL Website\fP\&. .SS Usage with RSA PKCS#1.5 or RSA PSS .SS Generating private and public key .sp First, the private key must be created: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl genrsa \-aes256 \-out priv.pem .ft P .fi .UNINDENT .UNINDENT .sp This asks for a passphrase. It is possible to retrieve the passphrase from a file \- of course, this must be protected against intrusion. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl genrsa \-aes256 \-passout file:passout \-out priv.pem .ft P .fi .UNINDENT .UNINDENT .sp The private key is used to export the public key with: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl rsa \-in priv.pem \-out public.pem \-outform PEM \-pubout .ft P .fi .UNINDENT .UNINDENT .sp “public.pem” contains the key in a format suitable for swupdate. The file can be passed to swupdate at the command line with the \-k parameter. .SS How to sign with RSA .sp Signing the image with rsa\-pkcs#1.5 is very simple: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl dgst \-sha256 \-sign priv.pem sw\-description > sw\-description.sig .ft P .fi .UNINDENT .UNINDENT .sp Signing the image with rsa\-pss is also very simple: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl dgst \-sha256 \-sign priv.pem \-sigopt rsa_padding_mode:pss \e \-sigopt rsa_pss_saltlen:\-2 sw\-description > sw\-description.sig .ft P .fi .UNINDENT .UNINDENT .SS Usage with certificates and CMS .SS Generating self\-signed certificates .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl req \-x509 \-newkey rsa:4096 \-nodes \-keyout mycert.key.pem \e \-out mycert.cert.pem \-subj \(dq/O=SWUpdate /CN=target\(dq .ft P .fi .UNINDENT .UNINDENT .sp Check the documentation for more information about parameters. The “mycert.key.pem” contains the private key and it is used for signing. It is \fInot\fP delivered on the target. .sp The target must have “mycert.cert.pem” installed \- this is used by SWUpdate for verification. .SS Using PKI issued certificates .sp It is also possible to use PKI issued code signing certificates. However, SWUpdate uses OpenSSL library for handling CMS signatures and the library requires the following attributes to be set on the signing certificate: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C keyUsage=digitalSignature extendedKeyUsage=emailProtection .ft P .fi .UNINDENT .UNINDENT .sp It is also possible to completely disable signing certificate key usage checking if this requirement cannot be satisfied. This is controlled by \fICONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE\fP configuration option. .SS How to sign with CMS .sp Signing the image is simple as in the previous case: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl cms \-sign \-in sw\-description \-out sw\-description.sig \-signer mycert.cert.pem \e \-inkey mycert.key.pem \-outform DER \-nosmimecap \-binary .ft P .fi .UNINDENT .UNINDENT .SS Building a signed SWU image .sp There are two files, sw\-description and its signature sw\-description.sig. The signature file must always directly follow the description file. .sp Each image inside sw\-description must have the attribute “sha256”, with the SHA256 sum of the image. If an image does not have the sha256 attribute, the whole compound image results as not verified and SWUpdate stops with an error before starting to install. .sp A simple script to create a signed image can be: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C #!/bin/bash MODE=\(dqRSA\-PKCS\-1.5\(dq PRODUCT_NAME=\(dqmyproduct\(dq CONTAINER_VER=\(dq1.0\(dq IMAGES=\(dqrootfs kernel\(dq FILES=\(dqsw\-description sw\-description.sig $IMAGES\(dq #if you use RSA if [ x\(dq$MODE\(dq == \(dqxRSA\-PKCS\-1.5\(dq ]; then openssl dgst \-sha256 \-sign priv.pem sw\-description > sw\-description.sig elif if [ x\(dq$MODE\(dq == \(dqxRSA\-PSS\(dq ]; then openssl dgst \-sha256 \-sign priv.pem \-sigopt rsa_padding_mode:pss \e \-sigopt rsa_pss_saltlen:\-2 sw\-description > sw\-description.sig else openssl cms \-sign \-in sw\-description \-out sw\-description.sig \-signer mycert.cert.pem \e \-inkey mycert.key.pem \-outform DER \-nosmimecap \-binary fi for i in $FILES;do echo $i;done | cpio \-ov \-H crc > ${PRODUCT_NAME}_${CONTAINER_VER}.swu .ft P .fi .UNINDENT .UNINDENT .SS Example for sw\-description with signed image .sp The example applies to a Beaglebone, installing Yocto images: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.1.0\(dq; hardware\-compatibility: [ \(dqrevC\(dq]; images: ( { filename = \(dqcore\-image\-full\-cmdline\-beaglebone.ext3\(dq; device = \(dq/dev/mmcblk0p2\(dq; type = \(dqraw\(dq; sha256 = \(dq43cdedde429d1ee379a7d91e3e7c4b0b9ff952543a91a55bb2221e5c72cb342b\(dq; } ); scripts: ( { filename = \(dqtest.lua\(dq; type = \(dqlua\(dq; sha256 = \(dqf53e0b271af4c2896f56a6adffa79a1ffa3e373c9ac96e00c4cfc577b9bea5f1\(dq; } ); } .ft P .fi .UNINDENT .UNINDENT .SS Running SWUpdate with signed images .sp Verification is activated by setting CONFIG_SIGNED_IMAGES in SWUpdate’s configuration. If activated, SWUpdate will always check the compound image. For security reasons, it is not possible to disable the check at runtime. The \-k parameter (public key file) is mandatory and the program stops if the public key is not passed. .SH SYMMETRICALLY ENCRYPTED UPDATE IMAGES .sp SWUpdate allows one to symmetrically encrypt update images using the AES block cipher in CBC mode. The following shows encryption with 256 bit key length but you may use other key lengths as well. .SS Building an Encrypted SWU Image .sp First, create a key; for aes\-256\-cbc we need 32 bytes of key and 16 bytes for an initialisation vector (IV). A complete documentation can be found at the \fI\%OpenSSL Website\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl rand \-hex 32 # key, for example 390ad54490a4a5f53722291023c19e08ffb5c4677a59e958c96ffa6e641df040 openssl rand \-hex 16 # IV, for example d5d601bacfe13100b149177318ebc7a4 .ft P .fi .UNINDENT .UNINDENT .sp Then, encrypt an image using this information via .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C openssl enc \-aes\-256\-cbc \-in \-out \-K \-iv .ft P .fi .UNINDENT .UNINDENT .sp where \fB\fP is the unencrypted source image file and \fB\fP is the encrypted output image file to be referenced in \fBsw\-description\fP\&. \fB\fP is the hex value part of the 2nd line of output from the key generation command above and \fB\fP is the hex value part of the 3rd line. .sp Then, create a key file to be supplied to SWUpdate via the \fI\-K\fP switch by putting the key and initialization vector hex values on one line separated by whitespace, e.g., for above example values .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 390ad54490a4a5f53722291023c19e08ffb5c4677a59e958c96ffa6e641df040 d5d601bacfe13100b149177318ebc7a4 .ft P .fi .UNINDENT .UNINDENT .sp Previous versions of SWUpdate allowed for a salt as third word in key file, that was never actually used for aes and has been removed. .sp You should change the IV with every encryption, see \fI\%CWE\-329\fP\&. The \fBivt\fP sw\-description attribute overrides the key file’s IV for one specific image. .SS Encryption of UBI volumes .sp Due to a limit in the Linux kernel API for UBI volumes, the size reserved to be written on disk should be declared before actually writing anything. .sp See the property “decrypted\-size” in UBI Volume Handler’s documentation. .SS Example sw\-description with Encrypted Image .sp The following example is a (minimal) \fBsw\-description\fP for installing a Yocto image onto a Beaglebone. Pay attention to the \fBencrypted = true;\fP setting. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C software = { version = \(dq0.0.1\(dq; images: ( { filename = \(dqcore\-image\-full\-cmdline\-beaglebone.ext3.enc\(dq; device = \(dq/dev/mmcblk0p3\(dq; encrypted = true; ivt = \(dq65D793B87B6724BB27954C7664F15FF3\(dq; } ); } .ft P .fi .UNINDENT .UNINDENT .SS Running SWUpdate with Encrypted Images .sp Symmetric encryption support is activated by setting the \fBENCRYPTED_IMAGES\fP option in SWUpdate’s configuration. Use the \fI\-K\fP parameter to provide the symmetric key file generated above to SWUpdate. .SS Decrypting with a PKCS#11 token .sp PKCS#11 support is activated by setting the \fBPKCS11\fP option in SWUpdate’s configuration. The key file has to have a PKCS#11 URL instead of the key then, containing at least the elements of this example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C pkcs11:slot\-id=42;id=%CA%FE%BA%BE?pin\-value=1234&module\-path=/usr/lib/libsofthsm2.so 65D793B87B6724BB27954C7664F15FF3 .ft P .fi .UNINDENT .UNINDENT .SH HANDLERS .SS Overview .sp It is quite difficult to foresee all possible installation cases. Instead of trying to find all use cases, SWUpdate let the developer free to add his own installer (that is, a new \fBhandler\fP), that must be responsible to install an image of a certain type. An image is marked to be of a defined type to be installed with a specific handler. .sp The parser make the connection between ‘image type’ and ‘handler’. It fills a table containing the list of images to be installed with the required handler to execute the installation. Each image can have a different installer. .SS Supplied handlers .INDENT 0.0 .TP .B In mainline there are the handlers for the most common cases. They include: .INDENT 7.0 .IP \(bu 2 flash devices in raw mode (both NOR and NAND) .IP \(bu 2 UBI volumes .IP \(bu 2 UBI volumes partitioner .IP \(bu 2 raw flashes handler (NAND, NOR, SPI\-NOR, CFI interface) .IP \(bu 2 disk partitioner .IP \(bu 2 raw devices, such as a SD Card partition .IP \(bu 2 bootloader (U\-Boot, GRUB, EFI Boot Guard) environment .IP \(bu 2 Lua scripts handler .IP \(bu 2 shell scripts handler .IP \(bu 2 rdiff handler .IP \(bu 2 readback handler .IP \(bu 2 archive (zo, tarballs) handler .IP \(bu 2 remote handler .IP \(bu 2 microcontroller update handler .UNINDENT .UNINDENT .sp For example, if an image is marked to be updated into a UBI volume, the parser must fill a supplied table setting “ubi” as required handler, and filling the other fields required for this handler: name of volume, size, and so on. .SS Creating own handlers .sp SWUpdate can be extended with new handlers. The user needs to register his own handler with the core and he must provide the callback that SWUpdate uses when an image required to be installed with the new handler. .sp The prototype for the callback is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int my_handler(struct img_type *img, void __attribute__ ((__unused__)) *data) .ft P .fi .UNINDENT .UNINDENT .sp The most important parameter is the pointer to a struct img_type. It describes a single image and inform the handler where the image must be installed. The file descriptor of the incoming stream set to the start of the image to be installed is also part of the structure. .sp The structure \fIimg_type\fP contains the file descriptor of the stream pointing to the first byte of the image to be installed. The handler must read the whole image, and when it returns back SWUpdate can go on with the next image in the stream. .sp The data parameter is usually a pointer that was registered with the handler. For script handlers it is instead a pointer to a \fBstruct script_handler_data\fP which contains a \fBscript_fn\fP enum value, indicating the current installation phase, and the registered data pointer. .sp SWUpdate provides a general function to extract data from the stream and copy to somewhere else: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int copyfile(int fdin, int fdout, int nbytes, unsigned long *offs, int skip_file, int compressed, uint32_t *checksum, unsigned char *hash); .ft P .fi .UNINDENT .UNINDENT .sp fdin is the input stream, that is img\->fdin from the callback. The \fIhash\fP, in case of signed images, is simply passed to copyfile() to perform the check, exactly as the \fIchecksum\fP parameter. copyfile() will return an error if checksum or hash do not match. The handler does not need to bother with them. How the handler manages the copied data, is specific to the handler itself. See supplied handlers code for a better understanding. .sp The handler’s developer registers his own handler with a call to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C __attribute__((constructor)) void my_handler_init(void) { register_handler(\(dqmytype\(dq, my_handler, my_mask, data); } .ft P .fi .UNINDENT .UNINDENT .sp SWUpdate uses the gcc constructors, and all supplied handlers are registered when SWUpdate is initialized. .sp register_handler has the syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C register_handler(my_image_type, my_handler, my_mask, data); .ft P .fi .UNINDENT .UNINDENT .sp Where: .INDENT 0.0 .IP \(bu 2 my_image_type : string identifying the own new image type. .IP \(bu 2 my_handler : pointer to the installer to be registered. .IP \(bu 2 my_mask : \fBHANDLER_MASK\fP enum value(s) specifying what input type(s) my_handler can process. .IP \(bu 2 data : an optional pointer to an own structure, that SWUpdate saves in the handlers’ list and pass to the handler when it will be executed. .UNINDENT .SS UBI Volume Handler .sp The UBI volume handler will update UBI volumes without changing the layout on the storage. Therefore, volumes must be created/adjusted beforehand. This can be done using the \fBpartitions\fP tag (see \fI\%partitions : UBI layout\fP). .sp The UBI volume handler will search for volumes in all MTD devices (unless blacklisted, see UBIBLACKLIST) to find the volume into which the image shall be installed. For this reason, \fBvolume names must be unique\fP within the system. Two volumes with the same name are not supported and will lead to unpredictable results (SWUpdate will install the image to the first volume with that name it finds, which may not be right one!). .sp When updating volumes, it is guaranteed that erase counters are preserved and not lost. The behavior of updating is identical to that of the \fBubiupdatevol(1)\fP tool from mtd\-utils. In fact, the same library from mtd\-utils (libubi) is reused by SWUpdate. .SS atomic volume renaming .sp The UBI volume handler has basic support for carrying out atomic volume renames by defining the \fBreplaces\fP property, which must contain a valid UBI volume name. After successfully updating the image to \fBvolume\fP, an atomic swap of the names of \fBvolume\fP and \fBreplaces\fP is done. Consider the following example .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename =\(dqu\-boot.img\(dq; volume =\(dqu\-boot_r\(dq; properties: { replaces = \(dqu\-boot\(dq; } } .ft P .fi .UNINDENT .UNINDENT .sp After u\-boot.img is successfully installed into the volume “u\-boot_r”, the volume “u\-boot_r” is renamed to “u\-boot” and “u\-boot” is renamed to “u\-boot_r”. .sp This mechanism allows one to implement a simple double copy update approach without the need of shared state with the bootloader. For example, the U\-Boot SPL can be configured to always load U\-Boot from the volume \fBu\-boot\fP without the need to access the environment. The volume replace functionality will ensure that this volume name always points to the currently valid volume. .sp However, please note the following limitations: .INDENT 0.0 .IP \(bu 2 Currently the rename takes place after \fIeach\fP image was installed successfully. Hence, it only makes sense to use this feature for images that are independent of the other installed images. A typical example is the bootloader. This behavior may be modified in the future to only carry out one atomic rename after all images were installed successfully. .IP \(bu 2 Atomic renames are only possible and permitted for volumes residing on the same UBI device. .UNINDENT .sp There is a handler ubiswap that allow one to do an atomic swap for several ubi volume after all the images were flashed. This handler is a script for the point of view of swudate, so the node that provide it the data should be added in the section scripts. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { type = \(dqubiswap\(dq; properties: { swap\-0 = [ \(dqboot\(dq , \(dq boot_r\(dq ]; swap\-1 = [ \(dqkernel\(dq , \(dqkernel_r\(dq ]; swap\-2 = [ \(dqrootfs\(dq , \(dqrootfs_r\(dq ]; }, }, ); .ft P .fi .UNINDENT .UNINDENT .sp WARNING: if you use the property replaces on an ubi volume that is also used with the handler ubiswap, this ubi volume will be swapped twice. It’s probably not what you want … .SS volume auto resize .sp The UBI volume handler has support to auto resize before flashing an image with the property \fBauto\-resize\fP\&. When this property is set on an image, the ubi volume is resized to fit exactly the image. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dqu\-boot.img\(dq; device = \(dqmtd0\(dq; volume = \(dqu\-boot_r\(dq; properties: { auto\-resize = \(dqtrue\(dq; } } .ft P .fi .UNINDENT .UNINDENT .sp WARNING: when this property is used, the device must be defined. .SS volume always remove .sp The UBI volume handler has support to always remove ubi volume before flashing with the property \fBalways\-remove\fP\&. When this property is set on an image, the ubi volume is always removed. This property should be used with property \fBauto\-resize\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dqu\-boot.img\(dq; device = \(dqmtd0\(dq; volume = \(dqu\-boot_r\(dq; properties: { always\-remove = \(dqtrue\(dq; auto\-resize = \(dqtrue\(dq; } } .ft P .fi .UNINDENT .UNINDENT .SS size properties .sp Due to a limit in the Linux kernel API for UBI volumes, the size reserved to be written on disk should be declared before actually writing anything. Unfortunately, the size of an encrypted or compressed image is not known until the decryption or decompression finished. This prevents correct declaration of the file size to be written on disk. .sp For this reason UBI images can declare the special properties “decrypted\-size” or “decompressed\-size” like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dqrootfs.ubifs.enc\(dq; volume = \(dqrootfs\(dq; encrypted = true; properties: { decrypted\-size = \(dq104857600\(dq; } }, { filename = \(dqhomefs.ubifs.gz\(dq; volume = \(dqhomefs\(dq; compressed = \(dqzlib\(dq; properties: { decompressed\-size = \(dq420000000\(dq; } } ); .ft P .fi .UNINDENT .UNINDENT .sp The real size of the image should be calculated and written to the sw\-description before assembling the cpio archive. In this example, 104857600 is the size of the rootfs after the decryption: the encrypted size is by the way larger. The decompressed size is of the homefs is 420000000. .sp The sizes are bytes in decimal notation. .SS Lua Handlers .sp In addition to the handlers written in C, it is possible to extend SWUpdate with handlers written in Lua that get loaded at SWUpdate startup. The Lua handler source code file may either be embedded into the SWUpdate binary via the \fBCONFIG_EMBEDDED_LUA_HANDLER\fP config option or has to be installed on the target system in Lua’s search path as \fBswupdate_handlers.lua\fP so that it can be loaded by the embedded Lua interpreter at run\-time. .sp In analogy to C handlers, the prototype for a Lua handler is .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\- Lua Handler. \-\- \-\-\- @param image img_type Lua equivalent of \(gastruct img_type\(ga \-\-\- @return number # 0 on success, 1 on error function lua_handler(image) ... end .ft P .fi .UNINDENT .UNINDENT .sp where \fBimage\fP is a Lua table (with attributes according to \fI\%sw\-description’s attribute reference\fP) that describes a single artifact to be processed by the handler (also see the Lua Handler Interface Specification in \fBhandlers/swupdate.lua\fP). .sp Note that dashes in the attributes’ names are replaced with underscores for the Lua domain to make them idiomatic, e.g., \fBinstalled\-directly\fP becomes \fBinstalled_directly\fP in the Lua domain. .sp For a script handler written in Lua, the prototype is .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\- Lua Handler. \-\- \-\-\- @param image img_type Lua equivalent of \(gastruct img_type\(ga \-\-\- @param scriptfn string Type, one of \(gapreinst\(ga or \(gapostinst\(ga \-\-\- @return number # 0 on success, 1 on error function lua_handler(image, scriptfn) ... end .ft P .fi .UNINDENT .UNINDENT .sp where \fBscriptfn\fP is either \fB\(dqpreinst\(dq\fP or \fB\(dqpostinst\(dq\fP\&. .sp To register a Lua handler, the \fBswupdate\fP module provides the \fBswupdate.register_handler()\fP method that takes the handler’s name, the Lua handler function to be registered under that name, and, optionally, the types of artifacts for which the handler may be called. If the latter is not given, the Lua handler is registered for all types of artifacts. The following call registers the above function \fBlua_handler\fP as \fImy_handler\fP which may be called for images: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C swupdate.register_handler(\(dqmy_handler\(dq, lua_handler, swupdate.HANDLER_MASK.IMAGE_HANDLER) .ft P .fi .UNINDENT .UNINDENT .sp A Lua handler may call C handlers (“chaining”) via the \fBswupdate.call_handler()\fP method. The callable and registered C handlers are available (as keys) in the table \fBswupdate.handler\fP\&. The following Lua code is an example of a simple handler chain\-calling the \fBrawfile\fP C handler: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\- Lua Handler. \-\- \-\-\- @param image img_type Lua equivalent of \(gastruct img_type\(ga \-\-\- @return number # 0 on success, 1 on error function lua_handler(image) if not swupdate.handler[\(dqrawfile\(dq] then swupdate.error(\(dqrawfile handler not available\(dq) return 1 end image.path = \(dq/tmp/destination.path\(dq local err, msg = swupdate.call_handler(\(dqrawfile\(dq, image) if err ~= 0 then swupdate.error(string.format(\(dqError chaining handlers: %s\(dq, msg)) return 1 end return 0 end .ft P .fi .UNINDENT .UNINDENT .sp Note that when chaining handlers and calling a C handler for a different type of artifact than the Lua handler is registered for, the \fBimage\fP table’s values must satisfy the called C handler’s expectations: Consider the above Lua handler being registered for “images” (\fBswupdate.HANDLER_MASK.IMAGE_HANDLER\fP) via the \fBswupdate.register_handler()\fP call shown above. As per the \fI\%sw\-description’s attribute reference\fP, the “images” artifact type doesn’t have the \fBpath\fP attribute but the “file” artifact type does. So, for calling the \fBrawfile\fP handler, \fBimage.path\fP has to be set prior to chain\-calling the \fBrawfile\fP handler, as done in the example above. Usually, however, no such adaptation is necessary if the Lua handler is registered for handling the type of artifact that \fBimage\fP represents. .sp In addition to calling C handlers, the \fBimage\fP table passed as parameter to a Lua handler has a \fBimage:copy2file()\fP method that implements the common use case of writing the input stream’s data to a file, which is passed as this method’s argument. On success, \fBimage:copy2file()\fP returns \fB0\fP or \fB\-1\fP plus an error message on failure. The following Lua code is an example of a simple handler calling \fBimage:copy2file()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\- Lua Handler. \-\- \-\-\- @param image img_type Lua equivalent of \(gastruct img_type\(ga \-\-\- @return number # 0 on success, 1 on error function lua_handler(image) local err, msg = image:copy2file(\(dq/tmp/destination.path\(dq) if err ~= 0 then swupdate.error(string.format(\(dqError calling copy2file: %s\(dq, msg)) return 1 end return 0 end .ft P .fi .UNINDENT .UNINDENT .sp Beyond using \fBimage:copy2file()\fP or chain\-calling C handlers, the \fBimage\fP table passed as parameter to a Lua handler has a \fBimage:read()\fP method that reads from the input stream and calls the Lua callback function \fB\fP for every chunk read, passing this chunk as parameter. On success, \fB0\fP is returned by \fBimage:read()\fP\&. On error, \fB\-1\fP plus an error message is returned. The following Lua code is an example of a simple handler printing the artifact’s content: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\- Lua Handler. \-\- \-\-\- @param image img_type Lua equivalent of \(gastruct img_type\(ga \-\-\- @return number # 0 on success, 1 on error function lua_handler(image) err, msg = image:read(function(data) print(data) end) if err ~= 0 then swupdate.error(string.format(\(dqError reading image: %s\(dq, msg)) return 1 end return 0 end .ft P .fi .UNINDENT .UNINDENT .sp Using the \fBimage:read()\fP method, an artifact’s contents may be (post\-)processed in and leveraging the power of Lua without relying on preexisting C handlers for the purpose intended. .sp Just as C handlers, a Lua handler must consume the artifact described in its \fBimage\fP parameter so that SWUpdate can continue with the next artifact in the stream after the Lua handler returns. Chaining handlers, calling \fBimage:copy2file()\fP, or using \fBimage:read()\fP satisfies this requirement. .sp The \fBswupdate\fP Lua module interface specification that details what functionality is made available to Lua handlers by SWUpdate’s \fBcorelib/lua_interface.c\fP is found in \fBhandlers/swupdate.lua\fP\&. It serves as reference, for mocking purposes, and type checking thanks to the EmmyLua\-inspired annotations. .sp Note that although the dynamic nature of Lua handlers would technically allow one to embed them into a to be processed \fB\&.swu\fP image, this is not implemented as it carries some security implications since the behavior of SWUpdate is changed dynamically. .SS Remote handler .sp Remote handlers are thought for binding legacy installers without having the necessity to rewrite them in Lua. The remote handler forward the image to be installed to another process, waiting for an acknowledge to be sure that the image is installed correctly. The remote handler makes use of the zeromq library \- this is to simplify the IPC with Unix Domain Socket. The remote handler is quite general, describing in sw\-description with the “data” attribute how to communicate with the external process. The remote handler always acts as client, and try a connect() using the socket identified by the “data” attribute. For example, a possible setup using a remote handler could be: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dqmyimage\(dq\(dq; type = \(dqremote\(dq; data = \(dqtest_remote\(dq; } ) .ft P .fi .UNINDENT .UNINDENT .sp The connection is instantiated using the socket \fBtest_remote\fP (according to the “data” field’s value) in the directory pointed to by the environment variable \fBTMPDIR\fP with \fB/tmp\fP as fall\-back if \fBTMPDIR\fP is not set. If \fBconnect()\fP fails, the remote handler signals that the update is not successful. Each zeromq message from SWUpdate is a multi\-part message split into two frames: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 first frame contains a string with a command. .IP \(bu 2 second frame contains data and can be of 0 bytes. .UNINDENT .UNINDENT .UNINDENT .sp There are currently just two possible commands: INIT and DATA. After a successful connect, SWUpdate sends the initialization string in the format: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C INIT: .ft P .fi .UNINDENT .UNINDENT .sp The external installer is informed about the size of the image to be installed, and it can assign resources if it needs. It will answer with the string \fIACK\fP or \fINACK\fP\&. The first NACK received by SWUpdate will interrupt the update. After sending the INIT command, the remote handler will send a sequence of \fIDATA\fP commands, where the second frame in message will contain chunks of the image to be installed. It is duty of the external process to take care of the amount of data transferred and to release resources when the last chunk is received. For each DATA message, the external process answers with a \fIACK\fP or \fINACK\fP message. .SS SWU forwarder .sp The SWU forwarder handler can be used to update other systems where SWUpdate is running. It can be used in case of master / slaves systems, where the master is connected to the network and the “slaves” are hidden to the external world. The master is then the only interface to the world. A general SWU can contain embedded SWU images as single artifacts, and the SWU handler will forward it to the devices listed in the description of the artifact. The handler can have a single “url” properties entry with an array of urls. Each url is the address of a secondary board where SWUpdate is running with webserver activated. The SWU handler expects to talk with SWUpdate’s embedded webserver. This helps to update systems where an old version of SWUpdate is running, because the embedded webserver is a common feature present in all versions. The handler will send the embedded SWU to all URLs at the same time, and setting \fBinstalled\-directly\fP is supported by this handler. [image] .sp The following example shows how to set a SWU as artifact and enables the SWU forwarder: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dqimage.swu\(dq; type = \(dqswuforward\(dq; properties: { url = [\(dqhttp://192.168.178.41:8080\(dq, \(dqhttp://192.168.178.42:8080\(dq]; }; }); .ft P .fi .UNINDENT .UNINDENT .SS rdiff handler .sp The rdiff handler adds support for applying binary delta patches generated by \fI\%librsync’s\fP rdiff tool. .sp Naturally, the smaller the difference between the diff’s source and target, the more effective is using this handler rather than shipping the full target, e.g., via the image handler. Hence, the most prominent use case for the rdiff handler is when having a read\-only root filesystem and applying a small update like security fixes or feature additions. If the sweet spot is crossed, an rdiff patch may even exceed the full target’s size due to necessary patch metadata. Also note that in order to be most effective, an image to be processed with rdiff should be built deterministic (see \fI\%reproducible\-builds.org\fP). .sp The rdiff algorithm requires no resources whatsoever on the device as the patch is fully computed in the backend. Consequently, the backend has to have knowledge of the current software running on the device in order to compute a sensible patch. Alike, the patch has to be applied on the device to an unmodified source as used in the backend for patch computation. This property is in particular useful for resource\-constrained devices as there’s no need for the device to, e.g., aid in the difference computation. .sp First, create the signature of the original (base) file via \fBrdiff signature \fP\&. Then, create the delta file (i.e., patch) from the original base file to the target file via \fBrdiff delta \fP\&. The \fB\fP is the artifact to be applied via this handler on the device. Essentially, it mimics running \fBrdiff patch \fP on the device. Naturally for patches, the very same \fB\fP has to be used for creating as well as for applying the patch to. .sp This handler registers itself for handling files and images. An exemplary sw\-description fragment for the files section is .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C files: ( { type = \(dqrdiff_file\(dq filename = \(dqfile.rdiff.delta\(dq; path = \(dq/usr/bin/file\(dq; } ); .ft P .fi .UNINDENT .UNINDENT .sp Note that the file referenced to by \fBpath\fP serves as \fB\fP and gets replaced by a temporary file serving as \fB\fP while the rdiff patch processing. .sp An exemplary sw\-description fragment for the images section is .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { type = \(dqrdiff_image\(dq; filename = \(dqimage.rdiff.delta\(dq; device = \(dq/dev/mmcblk0p2\(dq; properties: { rdiffbase = [\(dq/dev/mmcblk0p1\(dq]; }; } ); .ft P .fi .UNINDENT .UNINDENT .sp Here, the property \fBrdiffbase\fP qualifies the \fB\fP while the \fBdevice\fP attribute designates the \fB\fP\&. Note that there’s no support for the optional \fBoffset\fP attribute in the \fBrdiff_image\fP handler as there’s currently no apparent use case for it and skipping over unchanged content is handled well by the rdiff algorithm. .SS ucfw handler .sp This handler allows one to update the firmware on a microcontroller connected to the main controller via UART. Parameters for setup are passed via sw\-description file. Its behavior can be extended to be more general. The protocol is ASCII based. There is a sequence to be done to put the microcontroller in programming mode, after that the handler sends the data and waits for an ACK from the microcontroller. .sp The programming of the firmware shall be: .INDENT 0.0 .IP 1. 3 Enter firmware update mode (bootloader) .INDENT 3.0 .INDENT 3.5 .INDENT 0.0 .IP 1. 3 Set “reset line” to logical “low” .IP 2. 3 Set “update line” to logical “low” .IP 3. 3 Set “reset line” to logical “high” .UNINDENT .UNINDENT .UNINDENT .IP 2. 3 Send programming message .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $PROG;<> .ft P .fi .UNINDENT .UNINDENT .sp to the microcontroller. (microcontroller will remain in programming state) .INDENT 0.0 .IP 3. 3 microcontroller confirms with .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $READY;<> .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .IP 4. 3 Data transmissions package based from mainboard to microcontroller package definition: .INDENT 3.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 within a package the records are sent one after another without the end of line marker .IP \(bu 2 the package is completed with .UNINDENT .UNINDENT .UNINDENT .IP 5. 3 The microcontroller requests the next package with $READY;<> .IP 6. 3 Repeat step 4 and 5 until the complete firmware is transmitted. .IP 7. 3 The keypad confirms the firmware completion with $COMPLETED;<> .IP 8. 3 .INDENT 3.0 .TP .B Leave firmware update mode .INDENT 7.0 .IP 1. 3 Set “Update line” to logical “high” .IP 2. 3 Perform a reset over the “reset line” .UNINDENT .UNINDENT .UNINDENT .sp <> : checksum. The checksum is calculated as the two’s complement of the modulo\-256 sum over all bytes of the message string except for the start marker “$”. The handler expects to get in the properties the setup for the reset and prog gpios. They should be in this format: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C properties = { reset = \(dq::\(dq; prog = \(dq::\(dq; } .ft P .fi .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dqmicrocontroller\-image\(dq; type = \(dqucfw\(dq; device = \(dq/dev/ttymxc5\(dq; properties: { reset = \(dq/dev/gpiochip0:38:false\(dq; prog = \(dq/dev/gpiochip0:39:false\(dq; }; } ); .ft P .fi .UNINDENT .UNINDENT .SS SSBL Handler .sp This implements a way to switch two software sets using a duplicated structure saved on the flash (currently, only NOR flash is supported). Each of the two structures contains address and size of the image to be loaded by a first loader. A field contain the “age”, and it is incremented after each switch to show which is the active set. .SS Structure of SSBL Admin .TS center; |l|l|. _ T{ SSBL Magic Number (29 bit)Name T} T{ Age (3 bit) T} _ T{ Image Address Offset T} _ T{ Image Size T} _ .TE .sp The handler implements a post install script. First, it checks for consistency the two structures and find the active reading the first 32 bit value with a magic number and the age. It increments the age and saves the new structure in the inactive copy. After a reboot, the loader will check it and switch the software set. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { type = \(dqssblswitch\(dq; properties: { device = [\(dqmtdX\(dq, \(dqmtdY\(dq]; offset = [\(dq0\(dq, \(dq0\(dq]; imageoffs = [\(dq0x780000\(dq, \(dq0xA40000\(dq]; imagesize = [\(dq0x800000\(dq, \(dq0x800000\(dq]; } } .ft P .fi .UNINDENT .UNINDENT .sp Properties in sw\-description are all mandatory. They define where the SSBL Administration data are stored for both sets. Each properties is an array of two entries, containing values for each of the two SSBL administration. .SS Properties for SSBL handler .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ device T} T{ string T} T{ MTD device where the SSBL Admin Header is stored T} _ T{ offset T} T{ hex T} T{ Offset of SSBL header inside the MTD device T} _ T{ imageoffset T} T{ hex T} T{ Offset of the image to be loaded by a bootloader when this SSBL is set. T} _ T{ imagesize T} T{ hex T} T{ Size of the image to be loaded by a bootloader when this SSBL is set. T} _ .TE .SS Readback Handler .sp To verify that an image was written properly, this readback handler calculates the sha256 hash of a partition (or part of it) and compares it against a given hash value. .sp The following example explains how to use this handler: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { device = \(dq/dev/mmcblk2p1\(dq; type = \(dqreadback\(dq; properties: { sha256 = \(dqe7afc9bd98afd4eb7d8325196d21f1ecc0c8864d6342bfc6b6b6c84eac86eb42\(dq; size = \(dq184728576\(dq; offset = \(dq0\(dq; }; } ); .ft P .fi .UNINDENT .UNINDENT .sp Properties \fBsize\fP and \fBoffset\fP are optional, all the other properties are mandatory. .SS Properties for readback handler .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ device T} T{ string T} T{ The partition which shall be verified. T} _ T{ type T} T{ string T} T{ Identifier for the handler. T} _ T{ sha256 T} T{ string T} T{ Expected sha256 hash of the partition. T} _ T{ size T} T{ string T} T{ Data size (in bytes) to be verified. If 0 or not set, the handler will get the partition size from the device. T} _ T{ offset T} T{ string T} T{ Offset (in bytes) to the start of the partition. If not set, default value 0 will be used. T} _ .TE .SS Copy handler .sp The copy handler copies one source to a destination. It is a script handler, and no artifact in the SWU is associated with the handler. It can be used to copy configuration data, or parts that should be taken by the current installation. It requires the mandatory property (\fIcopyfrom\fP), while device contains the destination path. The handler performs a byte copy, and it does not matter which is the source \- it can be a file or a partition. An optional \fItype\fP field can set if the handler is active as pre or postinstall script. If not set, the handler is called twice. .SS Attributes for copy handler .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ device T} T{ string T} T{ If set, it is the destination. T} _ T{ type T} T{ string T} T{ One of “preinstall” or “postinstall” T} _ .TE .SS Properties for copy handler .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ size T} T{ string T} T{ Data size (in bytes) to be copied. If 0 or not set, the handler will try to find the size from the device. T} _ T{ chain T} T{ string T} T{ Handler to be called to install the data read from the “copyfrom” source. T} _ T{ recursive T} T{ string T} T{ Recursive copy if copyfrom is a directory (“true” or “false”) T} _ T{ create\- destination T} T{ string T} T{ Create the destination path if it does not exist (“true” or “false”) T} _ .TE .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts : ( { device = \(dq/dev/mmcblk2p1\(dq; type = \(dqcopy\(dq; properties : { copyfrom = \(dq/dev/mmcblk2p2\(dq; type = \(dqpostinstall\(dq; chain = \(dqraw\(dq; } } .ft P .fi .UNINDENT .UNINDENT .SS Bootloader handler .sp The bootloader handler allows to set bootloader’s environment with a file. The file shold have the format: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Comments are allowed using the hash char varname=value .ft P .fi .UNINDENT .UNINDENT .sp Empty lines are skipped. This simplifies the update of the whole environment instead of setting each variable inside the “bootenv” section in sw\-description. The property \fInooverride\fP allows to skip variables that are already set in sw\-description. If not set, variables set in bootenv are overwritten. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dquEnv.txt\(dq; type = \(dqbootloader\(dq; properties: { nooverride = \(dqtrue\(dq; } } ); bootenv: ( { name = \(dqbootenv01key\(dq; value = \(dqSOME VALUE\(dq; }); .ft P .fi .UNINDENT .UNINDENT .sp In the example above, bootenv01key is not overwritten by a value in uEnv.txt because the flag “nooverride” is set. .SS Archive handler .sp The archive handler extracts an archive to a destination path. It supports whatever format libarchive has been compiled to support, for example even if swupdate itself has no direct support for xz it can be possible to extract tar.xz files with it. .sp The attribute \fIpreserve\-attributes\fP must be set to preserve timestamps. uid/gid (numeric), permissions (except +x, always preserved) and extended attributes. .sp The property \fIcreate\-destination\fP can be set to the string \fItrue\fP to have swupdate create the destination path before extraction. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C files: ( { filename = \(dqexamples.tar.zst\(dq; type = \(dqarchive\(dq; path = \(dq/extract/here\(dq; preserve\-attributes = true; installed\-directly = true; properties: { create\-destination = \(dqtrue\(dq; } } ); .ft P .fi .UNINDENT .UNINDENT .SS Disk partitioner .sp This handler creates or modifies partitions using the library libfdisk. Handler must be put into the \fIpartitions\fP section of sw\-description. Setup for each partition is put into the \fIproperties\fP field of sw\-description. After writing the partition table it may create a file system on selected partitions. (Available only if CONFIG_DISKFORMAT is set.) .SS Properties for diskpart handler .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ labeltype T} T{ string T} T{ “gpt” or “dos” T} _ T{ nolock T} T{ string T} T{ “true” or “false” (default=false) This is like a force. If it is set, a lock failure will be ignored(lock will still be attempted). T} _ T{ noinuse T} T{ string T} T{ “true” or “false” (default=false) If set, it does not require the device to be not in use (mounted, etc.) T} _ T{ partition\-X T} T{ array T} T{ Array of values belonging to the partition number X T} _ .TE .sp For each partition, an array of couples key=value must be given. The following keys are supported: .SS Setup for a disk partition .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ size T} T{ string T} T{ Size of partition. K, M and G can be used for Kilobytes, Megabytes and Gigabytes. T} _ T{ start T} T{ integer T} T{ First sector for the partition T} _ T{ name T} T{ string T} T{ Name of the partition T} _ T{ type T} T{ string T} T{ Type of partition, it has two different meanings. It is the hex code for DOS (MBR) partition table or it is the string identifier in case of GPT. T} _ T{ dostype T} T{ string T} T{ Type of DOS (MBR) partition entry when using a table with a “gpt” labeltype. Using this option will create a hybrid MBR table. It is the hex code for DOS (MBR) partition table. This would typically be used when one wants to use a GPT formatted disk with a board that requires a dos table entry for initial bootstrapping. Note: A maximum of 3 partitions can have a dostype specified, this limit only applies to dos table entries and does not affect partitions without a dostype specified. T} _ T{ fstype T} T{ string T} T{ Optional filesystem type to be created on the partition. If no fstype key is given, no file will be created on the corresponding partition. vfat / ext2 / ext3 /ext4 / btrfs file system is supported T} _ T{ partuuid T} T{ string T} T{ The partition UUID (GPT only). If omitted, a UUID will be generated automatically. T} _ T{ flag T} T{ string T} T{ The following flags are supported: Dos Partition : “boot” set bootflag T} _ .TE .sp GPT example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C partitions: ( { type = \(dqdiskpart\(dq; device = \(dq/dev/sde\(dq; properties: { labeltype = \(dqgpt\(dq; partition\-1 = [ \(dqsize=64M\(dq, \(dqstart=2048\(dq, \(dqname=bigrootfs\(dq, \(dqtype=C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B\(dq]; partition\-2 = [\(dqsize=256M\(dq, \(dqstart=133120\(dq, \(dqname=ldata\(dq, \(dqtype=EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7\(dq, \(dqfstype=vfat\(dq]; partition\-3 = [\(dqsize=512M\(dq, \(dqstart=657408\(dq, \(dqname=log\(dq, \(dqfstype =ext4\(dq, 63DAF\-8483\-4772\-8E79\-3D69D8477DE4\(dq]; partition\-4 = [\(dqsize=4G\(dq, \(dqstart=1705984\(dq, \(dqname=system\(dq, \(dqtype=0FC63DAF\-8483\-4772\-8E79\-3D69D8477DE4\(dq]; partition\-5 = [\(dqsize=512M\(dq, \(dqstart=10094592\(dq, \(dqname=part5\(dq, \(dqtype=0FC63DAF\-8483\-4772\-8E79\-3D69D8477DE4\(dq]; } } .ft P .fi .UNINDENT .UNINDENT .sp MBR Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C partitions: ( { type = \(dqdiskpart\(dq; device = \(dq/dev/sde\(dq; properties: { labeltype = \(dqdos\(dq; partition\-1 = [ \(dqsize=64M\(dq, \(dqstart=2048\(dq, \(dqname=bigrootfs\(dq, \(dqtype=0x83\(dq]; partition\-2 = [\(dqsize=256M\(dq, \(dqstart=133120\(dq, \(dqname=ldata\(dq, \(dqtype=0x83\(dq]; partition\-3 = [\(dqsize=256M\(dq, \(dqstart=657408\(dq, \(dqname=log\(dq, \(dqtype=0x83\(dq]; partition\-4 = [\(dqsize=6G\(dq, \(dqstart=1181696\(dq, \(dqname=system\(dq, \(dqtype=0x5\(dq]; partition\-5 = [\(dqsize=512M\(dq, \(dqstart=1183744\(dq, \(dqname=part5\(dq, \(dqtype=0x83\(dq]; partition\-6 = [\(dqsize=512M\(dq, \(dqstart=2234368\(dq, \(dqname=part6\(dq, \(dqtype=0x83\(dq]; partition\-7 = [\(dqsize=16M\(dq, \(dqstart=3284992\(dq, \(dqname=part7\(dq, \(dqtype=0x6\(dq, \(dqfstype=vfat\(dq]; } } .ft P .fi .UNINDENT .UNINDENT .SS Toggleboot Handler .sp This handler is a script handler. It turns on the bootflag for one of a disk partition if the partition table is DOS. It reports an error if the table is GPT. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C script: ( { type = \(dqtoggleboot\(dq; device = \(dq/dev/sde\(dq; properties: { partition = \(dq1\(dq; } } .ft P .fi .UNINDENT .UNINDENT .SS gpt partition installer .sp There is a handler gptpart that allows writing an image into a gpt partition selected by the name. This handler do not modify the gpt partition (type, size, …), it just writes the image in the GPT partition. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C images: ( { filename = \(dqu\-boot.bin\(dq; type = \(dqgptpart\(dq; device = \(dq/dev/vdb\(dq; volume = \(dqu\-boot\-1\(dq; offset = \(dq1024\(dq; }, { filename = \(dqkernel.bin\(dq; type = \(dqgptpart\(dq; device = \(dq/dev/vdb\(dq; volume = \(dqkernel\-1\(dq; }, ); .ft P .fi .UNINDENT .UNINDENT .SS gpt partition swap .sp There is a handler gptswap that allow to swap gpt partitions after all the images were flashed. This handler only swaps the name of the partition. It coud be useful for a dual bank strategy. This handler is a script for the point of view of swupdate, so the node that provides it should be added in the section scripts. .sp Simple example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scripts: ( { type = \(dqgptswap\(dq; device = \(dq/dev/vdb\(dq; properties = { swap\-0 = [ \(dqu\-boot\-0\(dq , \(dqu\-boot\-1\(dq ]; swap\-1 = [ \(dqkernel\-0\(dq , \(dqkernel\-1\(dq ]; }; }, ); .ft P .fi .UNINDENT .UNINDENT .SS Diskformat Handler .sp This handler checks if the device already has a file system of the specified type. (Available only if CONFIG_DISKFORMAT is set.) If the file system does not yet exist, it will be created. In case an existing file system shall be overwritten, this can be achieved by setting the property \fBforce\fP to \fBtrue\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C partitions: ( { type = \(dqdiskformat\(dq; device = \(dq/dev/loop0p1\(dq; properties: { fstype = \(dqvfat\(dq; force = \(dqtrue\(dq; } }) .ft P .fi .UNINDENT .UNINDENT .SS Unique UUID Handler .sp This handler checks if the device already has a filesystems with a provide UUID. This is helpful in case the bootloader chooses the device to boot from the UUID and not from the partition number. One use case is with the GRUB bootloader when GRUB_DISABLE_LINUX_UUID is not set, as usual on Linux Distro as Debian or Ubuntu. .sp The handler iterates all UUIDs given in sw\-description and raises error if one of them is found on the device. It is a partition handler and it runs before any image is installed. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C partitions: ( { type = \(dquniqueuuid\(dq; properties: { fs\-uuid = [\(dq21f16cae\-612f\-4bc6\-8ef5\-e68cc9dc4380\(dq, \(dq18e12df1\-d8e1\-4283\-8727\-37727eb4261d\(dq]; } }); .ft P .fi .UNINDENT .UNINDENT .SS BTRFS Handler .sp This handler is activated if support for BTRFS is on. It allows to created and delete subvolumes during an update. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C partitions: ( { type = \(dqbtrfs\(dq; device = \(dq/dev/loop0p1\(dq; properties: { command = < one of create\(dq or \(dqdelete\(dq > path = ; mount = \(dqtrue\(dq or missing; } }) .ft P .fi .UNINDENT .UNINDENT .sp If \fImount\fP is set, SWUpdate will mount the device and the path is appenden to the mountpoint used with mount. If device is already mounted, path is the absolute path. .SS Delta Update Handler .sp The handler processes a ZCHUNK header and finds which chunks should be downloaded after generating the corresponding header of the running artifact to be updated. The handler uses just a couple of attributes from the main setup, and gets more information from the properties. The attributes are then passed to a secondary handler that will install the artefact after the delta handler has assembled it. The handler requires ZST because this is the compression format for Zchunk. .sp The SWU must just contain the ZCK’s header, while the ZCK file is put as it is on the server. The utilities in Zchunk project are used to build the zck file. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C zck \-u \-h sha256 .ft P .fi .UNINDENT .UNINDENT .sp This will generates a file .zck. To extract the header, use the \fIzck_read_header\fP utility: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C HSIZE=\(gazck_read_header \-v .zck | grep \(dqHeader size\(dq | cut \-d\(aq:\(aq \-f2\(ga dd if=.zck of=.header bs=1 count=$((HSIZE)) .ft P .fi .UNINDENT .UNINDENT .sp The resulting header file must be packed inside the SWU. .SS Properties for delta update handler .TS center; |l|l|l|. _ T{ Name T} T{ Type T} T{ Description T} _ T{ url T} T{ string T} T{ This is the URL from where the handler will download the missing chunks. The server must support byte range header. T} _ T{ source T} T{ string T} T{ name of the device or file to be used for the comparison. T} _ T{ chain T} T{ string T} T{ this is the name (type) of the handler that is called after reassembling the artifact. T} _ T{ max\-ranges T} T{ string T} T{ Max number of ranges that a server can accept. Default value (150) should be ok for most servers. T} _ T{ zckloglevel T} T{ string T} T{ this sets the log level of the zcklib. Logs are intercepted by SWupdate and appear in SWUpdate’s log. Value is one of debug,info warn,error,none T} _ T{ debug\-chunks T} T{ string T} T{ “true”, default is not set. This activates more verbose debugging output and the list of all chunks is printed, and it reports if a chunk is downloaded or copied from the source. T} _ T{ source\-size T} T{ string T} T{ This limits the index of the source It is helpful in case of filesystem in much bigger partition. It has the value for the size or it can be set to “detect” and the handler will try to find the effective size of fs. T} _ .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { filename = \(dqsoftware.header\(dq; type = \(dqdelta\(dq; device = \(dq/dev/mmcblk0p2\(dq; properties: { url = \(dqhttp://examples.com/software.zck\(dq; chain = \(dqraw\(dq; source = \(dq/dev/mmcblk0p3\(dq; zckloglevel = \(dqerror\(dq; /* debug\-chunks = \(dqtrue\(dq; */ }; } .ft P .fi .UNINDENT .UNINDENT .SS Memory issue with zchunk .sp SWUpdate will create the header from the current version, often from a block partition. As default, Zchunk creates a temporary file with all chunks in /tmp, that is at the end concatenated to the header and written to the destination file. This means that an amount of memory equal to the partition (SWUpdate does not compress the chunks) is required. This was solved with later version of Zchunk \- check inside zchunk code if ZCK_NO_WRITE is supported. .SH MONGOOSE DAEMON MODE .SS Introduction .sp Mongoose is a daemon mode of SWUpdate that provides a web server, web interface and web application. .sp The web application in \fBweb\-app\fP uses the \fI\%Node.js\fP package manager and \fI\%gulp\fP as build tool. It depends on \fI\%Bootstrap 4\fP, \fI\%Font Awesome 5\fP and \fI\%Dropzone.js\fP\&. .SS Startup .sp After having configured and compiled SWUpdate with enabled mongoose web server: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&./swupdate \-\-help .ft P .fi .UNINDENT .UNINDENT .sp lists the mandatory and optional arguments to be provided to mongoose. As an example, .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&./swupdate \-l 5 \-w \(aq\-r ./examples/www/v2 \-p 8080\(aq \-p \(aqreboot\(aq .ft P .fi .UNINDENT .UNINDENT .sp runs SWUpdate in mongoose daemon mode with log\-level \fBTRACE\fP and a web server at \fI\%http://localhost:8080\fP\&. .SS Example .sp The ready to use example of the web application in the \fBexamples/www/v2\fP directory uses a Public Domain \fIbackground.jpg\fP image from \fI\%pixabay\fP with is released under the Creative Commons CC0 license. The used \fIfavicon.png\fP and \fIlogo.png\fP images are made from the SWUpdate logo and therefore subject to the GNU General Public License version 2. You must comply to this license or replace the images with your own files. .SS Customize .sp You could customize the web application inside the \fBweb\-app\fP directory. Beside the replace of the \fIfavicon.png\fP, \fIlogo.png\fP and \fIbackground.jpg\fP images inside the \fBimages\fP directory you could customize the Bootstrap colors and settings inside the \fBscss/bootstrap.scss\fP style sheet. The style sheet changes need a rebuild of the web application source code. .SS Develop .sp The development requires Node.js version 6 or greater and a prebuilt SWUpdate project with enabled mongoose web server and web application interface version 2 support. .INDENT 0.0 .IP 1. 3 Enter the web application directory: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C cd ./web\-app .ft P .fi .UNINDENT .UNINDENT .IP 2. 3 Install the dependencies: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C npm install .ft P .fi .UNINDENT .UNINDENT .IP 3. 3 Build the web application: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C npm run build .ft P .fi .UNINDENT .UNINDENT .IP 4. 3 Start the web application: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C \&../swupdate \-w \(aq\-r ./dist \-p 8080\(aq \-p \(aqecho reboot\(aq .ft P .fi .UNINDENT .UNINDENT .IP 5. 3 Test the web application: .INDENT 3.0 .INDENT 3.5 \fI\%http://localhost:8080/\fP .UNINDENT .UNINDENT .IP 6. 3 Pack the web application (optional): .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C npm run package \-\- \-\-output swupdate\-www.tar.gz .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Contribute .sp Please run the linter before any commit .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C npm run lint .ft P .fi .UNINDENT .UNINDENT .SH SURICATTA DAEMON MODE .SS Introduction .sp Suricatta is – like mongoose – a daemon mode of SWUpdate, hence the name suricatta (engl. meerkat) as it belongs to the mongoose family. .sp Suricatta regularly polls a remote server for updates, downloads, and installs them. Thereafter, it reboots the system and reports the update status to the server, based on an update state variable currently stored in bootloader’s environment ensuring persistent storage across reboots. Some U\-Boot script logics or U\-Boot’s \fBbootcount\fP feature may be utilized to alter this update state variable, e.g., by setting it to reflect failure in case booting the newly flashed root file system has failed and a switchback had to be performed. .sp Suricatta is designed to be extensible in terms of the servers supported as described in Section \fI\%The Suricatta Interface\fP\&. Currently, support for the \fI\%hawkBit\fP server is implemented via the \fI\%hawkBit Direct Device Integration API\fP alongside a simple general purpose HTTP server. The support for suricatta modules written in Lua is not a particular server support implementation but rather an option for writing such in Lua instead of C. .SS Running suricatta .sp After having configured and compiled SWUpdate with enabled suricatta support for hawkBit, .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&./swupdate \-\-help .ft P .fi .UNINDENT .UNINDENT .sp lists the mandatory and optional arguments to be provided to suricatta when using hawkBit as server. As an example, .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&./swupdate \-l 5 \-u \(aq\-t default \-u http://10.0.0.2:8080 \-i 25\(aq .ft P .fi .UNINDENT .UNINDENT .sp runs SWUpdate in suricatta daemon mode with log\-level \fBTRACE\fP, polling a hawkBit instance at \fBhttp://10.0.0.2:8080\fP with tenant \fBdefault\fP and device ID \fB25\fP\&. .sp If multiple server support is compiled in, the \fB\-S\fP / \fB\-\-server\fP option or a \fBserver\fP entry in the configuration file’s \fB[suricatta]\fP section selects the one to use at run\-time. For convenience, when having support for just one server compiled\-in, this is chosen automatically. .sp Note that on startup when having installed an update, suricatta tries to report the update status to its upstream server, e.g., hawkBit, prior to entering the main loop awaiting further updates. If this initial report fails, e.g., because of a not (yet) configured network or a currently unavailable hawkBit server, SWUpdate may exit with an according error code. This behavior allows one to, for example, try several upstream servers sequentially. If suricatta should keep retrying until the update status is reported to its upstream server irrespective of the error conditions, this has to be realized externally in terms of restarting SWUpdate on exit. .sp After an update has been performed, an agent listening on the progress interface may execute post\-update actions, e.g., a reboot, on receiving \fBDONE\fP\&. Additionally, a post\-update command specified in the configuration file or given by the \fB\-p\fP command line option can be executed. .sp Note that at least a restart of SWUpdate has to be performed as post\-update action since only then suricatta tries to report the update status to its upstream server. Otherwise, succinct update actions announced by the upstream server are skipped with an according message until a restart of SWUpdate has happened in order to not install the same update again. .SS The Suricatta Interface .sp Support for servers other than hawkBit or the general purpose HTTP server can be realized by implementing the “interfaces” described in \fBinclude/channel.h\fP and \fBinclude/suricatta/server.h\fP, the latter either in C or in Lua. The channel interface abstracts a particular connection to the server, e.g., HTTP\-based in case of hawkBit. The server interface defines the logics to poll and install updates. See \fBcorelib/channel_curl.c\fP / \fBinclude/channel_curl.h\fP and \fBsuricatta/server_hawkbit.{c,h}\fP for an example implementation in C targeted towards hawkBit. .sp \fBinclude/channel.h\fP describes the functionality a channel has to implement: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C typedef struct channel channel_t; struct channel { ... }; channel_t *channel_new(void); .ft P .fi .UNINDENT .UNINDENT .sp which sets up and returns a \fBchannel_t\fP struct with pointers to functions for opening, closing, fetching, and sending data over the channel. .sp \fBinclude/suricatta/server.h\fP describes the functionality a server has to implement: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C typedef struct { server_op_res_t has_pending_action(int *action_id); server_op_res_t install_update(void); server_op_res_t send_target_data(void); unsigned int get_polling_interval(void); server_op_res_t start(const char *cfgfname, int argc, char *argv[]); server_op_res_t stop(void); server_op_res_t ipc(int fd); void (*help)(void); } server_t; .ft P .fi .UNINDENT .UNINDENT .sp These functions constituting a particular suricatta server implementation have to be registered for being selectable at run\-time by calling \fBregister_server()\fP (see \fBinclude/suricatta/server.h\fP) with a name and a \fBserver_t\fP struct pointer implemented in a \fB__attribute__((constructor))\fP marked function, see \fBsuricatta/server_hawkbit.c\fP as example. .sp The type \fBserver_op_res_t\fP is defined in \fBinclude/suricatta/suricatta.h\fP\&. It represents the valid function return codes for a server’s implementation. .sp In addition to implementing the particular channel and server, the \fBsuricatta/Config.in\fP file has to be adapted to include a new option so that the new implementation becomes selectable in SWUpdate’s configuration. In the simplest case, adding an option like the following one for hawkBit into the \fBmenu \(dqServer\(dq\fP section is sufficient. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C config SURICATTA_HAWKBIT bool \(dqhawkBit support\(dq default y select JSON help Support for hawkBit server. https://projects.eclipse.org/projects/iot.hawkbit .ft P .fi .UNINDENT .UNINDENT .sp Having included the new server implementation into the configuration, edit \fBsuricatta/Makefile\fP to specify the implementation’s linkage into the SWUpdate binary, e.g., for the hawkBit example implementation, the following lines add \fBserver_hawkbit.o\fP to the resulting SWUpdate binary if \fBSURICATTA_HAWKBIT\fP was selected while configuring SWUpdate. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ifneq ($(CONFIG_SURICATTA_HAWKBIT),) obj\-$(CONFIG_SURICATTA) += server_hawkbit.o endif .ft P .fi .UNINDENT .UNINDENT .SS Support for general purpose HTTP server .sp This is a very simple backend that uses standard HTTP response codes to signal if an update is available. There are closed source backends implementing this interface, but because the interface is very simple interface, this server type is also suitable for implementing an own backend server. For inspiration, there’s a simple (mock) server implementation available in \fBexamples/suricatta/server_general.py\fP\&. .sp The API consists of a GET with Query parameters to inform the server about the installed version. The query string has the format: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C http(s)://?param1=val1¶m2=value2... .ft P .fi .UNINDENT .UNINDENT .sp As examples for parameters, the device can send its serial number, MAC address and the running version of the software. It is duty of the backend to interpret this \- SWUpdate just takes them from the “identify” section of the configuration file and encodes the URL. .sp The server answers with the following return codes: .TS center; |l|l|l|. _ T{ HTTP Code T} T{ Text T} T{ Description T} _ T{ 302 T} T{ Found T} T{ A new software is available at URL in the Location header T} _ T{ 400 T} T{ Bad Request T} T{ Some query parameters are missing or in wrong format T} _ T{ 403 T} T{ Forbidden T} T{ Client certificate not valid T} _ T{ 404 T} T{ Not found T} T{ No update is available for this device T} _ T{ 503 T} T{ Unavailable T} T{ An update is available but server can’t handle another update process now. T} _ .TE .sp Server’s answer can contain the following headers: .TS center; |l|l|l|. _ T{ Header’s name T} T{ Codes T} T{ Description T} _ T{ Retry\-after T} T{ 503 T} T{ Contains a number which tells the device how long to wait until ask the next time for updates. (Seconds) T} _ T{ Content\-MD5 T} T{ 302 T} T{ Contains the checksum of the update file which is available under the url of location header T} _ T{ Location T} T{ 302 T} T{ URL where the update file can be downloaded. T} _ .TE .sp The device can send logging data to the server. Any information is transmitted in a HTTP PUT request with the data as plain string in the message body. The Content\-Type Header need to be set to text/plain. .sp The URL for the logging can be set as separate URL in the configuration file or via –logurl command line parameter: .sp The device sends data in a CSV format (Comma Separated Values). The format is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C value1,value2,... .ft P .fi .UNINDENT .UNINDENT .sp The format can be specified in the configuration file. A \fIformat\fP For each \fIevent\fP can be set. The supported events are: .TS center; |l|l|. _ T{ Event T} T{ Description T} _ T{ check T} T{ dummy. It could send an event each time the server is polled. T} _ T{ started T} T{ A new software is found and SWUpdate starts to install it T} _ T{ success T} T{ A new software was successfully installed T} _ T{ fail T} T{ Failure by installing the new software T} _ .TE .sp The \fIgeneral server\fP has an own section inside the configuration file. As example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C gservice = { url = ....; logurl = ; logevent : ( {event = \(dqcheck\(dq; format=\(dq#2,date,fw,hw,sp\(dq}, {event = \(dqstarted\(dq; format=\(dq#12,date,fw,hw,sp\(dq}, {event = \(dqsuccess\(dq; format=\(dq#13,date,fw,hw,sp\(dq}, {event = \(dqfail\(dq; format=\(dq#14,date,fw,hw,sp\(dq} ); } .ft P .fi .UNINDENT .UNINDENT .sp \fIdate\fP is a special field and it is interpreted as localtime in RFC 2822 format. Each Comma Separated field is looked up inside the \fIidentify\fP section in the configuration file, and if a match is found the substitution occurs. In case of no match, the field is sent as it is. For example, if the identify section has the following values: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C identify : ( { name = \(dqsp\(dq; value = \(dq333\(dq; }, { name = \(dqhw\(dq; value = \(dqipse\(dq; }, { name = \(dqfw\(dq; value = \(dq1.0\(dq; } ); .ft P .fi .UNINDENT .UNINDENT .sp with the events set as above, the formatted text in case of “success” will be: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Formatted log: #13,Mon, 17 Sep 2018 10:55:18 CEST,1.0,ipse,333 .ft P .fi .UNINDENT .UNINDENT .SS Support for Suricatta Modules in Lua .sp The \fBserver_lua.c\fP C\-to\-Lua bridge enables writing suricatta modules in Lua. It provides the infrastructure in terms of the interface to SWUpdate “core” to the Lua realm, enabling the “business logic” such as handling update flows and communicating with backend server APIs to be modeled in Lua. To the Lua realm, the \fBserver_lua.c\fP C\-to\-Lua bridge provides the same functionality as the other suricatta modules written in C have, realizing a separation of means and control. Effectively, it lifts the interface outlined in Section \fI\%The Suricatta Interface\fP to the Lua realm. .sp As an example server implementation, see \fBexamples/suricatta/server_general.py\fP for a simple (mock) server of a backend that’s modeled after the “General Purpose HTTP Server” (cf. Section \fI\%Support for general purpose HTTP server\fP). The matching Lua suricatta module is found in \fBexamples/suricatta/swupdate_suricatta.lua\fP\&. Place it in Lua’s path so that a \fBrequire(\(dqswupdate_suricatta\(dq)\fP can load it or embed it into the SWUpdate binary by enabling \fBCONFIG_EMBEDDED_SURICATTA_LUA\fP and setting \fBCONFIG_EMBEDDED_SURICATTA_LUA_SOURCE\fP accordingly. .sp The interface specification in terms of a Lua (suricatta) module is found in \fBsuricatta/suricatta.lua\fP\&. .SS \fIsuricatta\fP .sp The \fBsuricatta\fP table is the module’s main table housing the exposed functions and definitions via the sub\-tables described below. In addition, the main functions \fBsuricatta.install()\fP and \fBsuricatta.download()\fP as well as the convenience functions \fBsuricatta.getversion()\fP, \fBsuricatta.sleep()\fP, and \fBsuricatta.get_tmpdir()\fP are exposed: .sp The function \fBsuricatta.install(install_channel)\fP installs an update artifact from a remote server or a local file. The \fBinstall_channel\fP table parameter designates the channel to be used for accessing the artifact plus channel options diverging from the defaults set at channel creation time. For example, an \fBinstall_channel\fP table may look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { channel = chn, url = \(dqhttps://artifacts.io/update.swu\(dq } .ft P .fi .UNINDENT .UNINDENT .sp where \fBchn\fP is the return value of a call to \fBchannel.open()\fP\&. The other table attributes, like \fBurl\fP in this example, are channel options diverging from or omitted while channel creation time, see \fI\%suricatta.channel\fP\&. For installing a local file, an \fBinstall_channel\fP table may look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { channel = chn, url = \(dqfile:///path/to/file.swu\(dq } .ft P .fi .UNINDENT .UNINDENT .sp The function \fBsuricatta.download(download_channel, localpath)\fP just downloads an update artifact. The parameter \fBdownload_channel\fP is as for \fBsuricatta.install()\fP\&. The parameter \fBlocalpath\fP designates the output path for the artifact. The \fBsuricatta.get_tmpdir()\fP function (see below) is in particular useful for this case to supply a temporary download location as \fBlocalpath\fP\&. A just downloaded artifact may be installed later using \fBsuricata.install()\fP with an appropriate \fBfile://\fP URL, realizing a deferred installation. .sp Both, \fBsuricatta.install()\fP and \fBsuricatta.download()\fP return \fBtrue\fP, or, in case of error, \fBnil\fP, a \fBsuricatta.status\fP value, and a table with messages in case of errors, else an empty table. .nf .fi .sp .sp The function \fBsuricatta.getversion()\fP returns a table with SWUpdate’s \fBversion\fP and \fBpatchlevel\fP fields. This information can be used to determine API (in\-)compatibility of the Lua suricatta module with the SWUpdate version running it. .sp The function \fBsuricatta.sleep(seconds)\fP is a wrapper around \fISLEEP(3)\fP for, e.g., implementing a REST API call retry mechanism after a number of given seconds have elapsed. .sp The function \fBsuricatta.get_tmpdir()\fP returns the path to SWUpdate’s temporary working directory where, e.g., the \fBsuricatta.download()\fP function may place the downloaded artifacts. .SS \fIsuricatta.status\fP .sp The \fBsuricatta.status\fP table exposes the \fBserver_op_res_t\fP enum values defined in \fBinclude/util.h\fP to the Lua realm. .SS \fIsuricatta.notify\fP .sp The \fBsuricatta.notify\fP table provides the usual logging functions to the Lua suricatta module matching their uppercase\-named pendants available in the C realm. .sp One notable exception is \fBsuricatta.notify.progress(message)\fP which dispatches the message to the progress interface (see \fI\%Getting information on running update\fP). Custom progress client implementations listening and acting on custom progress messages can be realized using this function. .sp All notify functions return \fBnil\fP\&. .SS \fIsuricatta.pstate\fP .sp The \fBsuricatta.pstate\fP table provides a binding to SWUpdate’s (persistent) state handling functions defined in \fBinclude/state.h\fP, however, limited to the bootloader environment variable \fBSTATE_KEY\fP defined by \fBCONFIG_UPDATE_STATE_BOOTLOADER\fP and defaulting to \fBustate\fP\&. In addition, it captures the \fBupdate_state_t\fP enum values. .sp The function \fBsuricatta.pstate.save(state)\fP requires one of \fBsuricatta.pstate\fP’s “enum” values as parameter and returns \fBtrue\fP, or, in case of error, \fBnil\fP\&. The function \fBsuricatta.pstate.get()\fP returns one of \fBsuricatta.pstate\fP’s “enum” values or, in case of error, \fBSTATE_ERROR\fP\&. .SS \fIsuricatta.server\fP .sp The \fBsuricatta.server\fP table provides the sole function \fBsuricatta.server.register(function_p, purpose)\fP\&. It registers a Lua function “pointed” to by \fBfunction_p\fP for the purpose \fBpurpose\fP which is defined by \fBsuricatta.server\fP’s “enum” values. Those enum values correspond to the functions defined in the interface outlined in the Section on \fI\%The Suricatta Interface\fP\&. .sp In addition to these functions, the two callback functions \fBCALLBACK_PROGRESS\fP and \fBCALLBACK_CHECK_CANCEL\fP can be registered optionally: The former can be used to upload progress information to the server while the latter serves as \fBdwlwrdata\fP function (see \fBinclude/channel_curl.h\fP) to decide on whether an installation should be aborted while the download phase. .sp For details on the (callback) functions and their signatures, see the interface specification \fBsuricatta/suricatta.lua\fP and the documented example Lua suricatta module found in \fBexamples/suricatta/swupdate_suricatta.lua\fP\&. .sp The \fBsuricatta.server.register()\fP function returns \fBtrue\fP, or, in case of error, \fBnil\fP\&. .SS \fIsuricatta.channel\fP .sp The \fBsuricatta.channel\fP table captures channel handling for suricatta Lua modules. The single function \fBsuricatta.channel.open(options)\fP creates and opens a channel to a server. Its single parameter \fBoptions\fP is a table specifying the channel’s default options such as \fIproxy\fP, \fIretries\fP, \fIusessl\fP, \fIstrictssl\fP, or \fIheaders_to_send\fP\&. For convenience, options that may change per request such as \fIurl\fP, \fIcontent\-type\fP, or \fIheaders_to_send\fP may be set as defaults on channel creation time while being selectively overruled on a per request basis. The channel options currently supported to be set are listed in the \fBsuricatta.channel.options\fP table. In essence, the \fBoptions\fP parameter table is the Lua table equivalent of \fBinclude/channel_curl.h\fP’s \fBchannel_data_t\fP\&. .sp The \fBsuricatta.channel.open(options)\fP function returns a channel table which is either passed to the \fBsuricatta.install()\fP and \fBsuricatta.download()\fP functions or used directly for communication with a server. More specifically, it has the three functions .INDENT 0.0 .IP \(bu 2 \fBget(options)\fP for retrieving information from the server, .IP \(bu 2 \fBput(options)\fP for sending information to the server, and .IP \(bu 2 \fBclose()\fP for closing the channel. .UNINDENT .sp The \fBget()\fP and \fBput()\fP functions’ single parameter \fBoptions\fP is a per\-request channel option table as described above. .sp The functions \fBget()\fP and \fBput()\fP return \fBtrue\fP, or, in case of error, \fBnil\fP, a \fBsuricatta.status\fP value, and an operation result table. The latter contains the fields: .INDENT 0.0 .IP \(bu 2 \fBhttp_response_code\fP carrying the HTTP error code, .IP \(bu 2 \fBformat\fP as one of \fBsuricatta.channel.content\fP’s options, .IP \(bu 2 \fBraw_reply\fP if \fBoptions\fP contained \fBformat = suricatta.channel.content.RAW\fP, .IP \(bu 2 \fBjson_reply\fP if \fBoptions\fP contained \fBformat = suricatta.channel.content.JSON\fP, and .IP \(bu 2 the HTTP headers received in the \fBreceived_headers\fP table, if any. .UNINDENT .sp The \fBsuricatta.channel.content\fP “enum” table defines the “format”, i.e., the response body content type and whether to parse it or not: .INDENT 0.0 .IP \(bu 2 \fBNONE\fP means the response body is discarded. .IP \(bu 2 \fBRAW\fP means the raw server’s reply is available as \fBraw_reply\fP\&. .IP \(bu 2 \fBJSON\fP means the server’s JSON reply is parsed into a Lua table and available as \fBjson_reply\fP\&. .UNINDENT .sp The \fBsuricatta.channel.method\fP “enum” table defines the HTTP method to use for a request issued with the \fBput(options)\fP function, i.e., \fIPOST\fP, \fIPATCH\fP, or \fIPUT\fP as specified in the \fBoptions\fP parameter table via the \fBmethod\fP attribute. In addition to the HTTP method, the request body’s content is set with the \fBrequest_body\fP attribute in the \fBoptions\fP parameter table. .sp As a contrived example, consider the following call to a channel’s \fBput()\fP function .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&... local res, _, data = channel.put({ url = string.format(\(dq%s/%s\(dq, base_url, device_id), content_type = \(dqapplication/json\(dq, method = suricatta.channel.method.PATCH, format = suricatta.channel.content.NONE, request_body = \(dq{ ... }\(dq }) \&... .ft P .fi .UNINDENT .UNINDENT .sp that issues a HTTP \fIPATCH\fP to some URL with a JSON content without having interest in the response body. .sp More examples of how to use a channel can be found in the example suricatta Lua module \fBexamples/suricatta/swupdate_suricatta.lua\fP\&. .SS \fIsuricatta.bootloader\fP .sp The \fBsuricatta.bootloader\fP table exposes SWUpdate’s bootloader environment modification functions to suricatta Lua modules. .sp The enum\-like table \fBsuricatta.bootloader.bootloaders\fP holds the bootloaders SWUpdate supports, i.e. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C suricatta.bootloader.bootloaders = { EBG = \(dqebg\(dq, NONE = \(dqnone\(dq, GRUB = \(dqgrub\(dq, UBOOT = \(dquboot\(dq, }, .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp The function \fBsuricatta.bootloader.get()\fP returns the currently selected bootloader in terms of a \fBsuricatta.bootloader.bootloaders\fP field value. .sp The function \fBsuricatta.bootloader.is(name)\fP takes one of \fBsuricatta.bootloader.bootloaders\fP’s field values as \fBname\fP and returns \fBtrue\fP if it is the currently selected bootloader, \fBfalse\fP otherwise. .sp The functions in the \fBsuricatta.bootloader.env\fP table interact with the currently selected bootloader’s environment: .sp The function \fBsuricatta.bootloader.env.get(variable)\fP retrieves the value associated to \fBvariable\fP from the bootloader’s environment. .sp The function \fBsuricatta.bootloader.env.set(variable, value)\fP sets the bootloader environment’s key \fBvariable\fP to \fBvalue\fP\&. .sp The function \fBsuricatta.bootloader.env.unset(variable)\fP deletes the bootloader environment’s key \fBvariable\fP\&. .sp The function \fBsuricatta.bootloader.env.apply(filename)\fP applies all key=value lines of a local file \fBfilename\fP to the currently selected bootloader’s environment. .SH CONFIG FOR HAWKBIT UNDER SSL/TLS USING PRIVATE CA / SUB CA .sp A user\-contributed recipe based on hawkBit (0.2.0\-SNAPSHOT) + swupdate (v2018.03) .SS Purpose .sp Use HTTPS on a hawkBit server to avoid server spoofing. Anonymous client connections are authorized. .SS Recipe .INDENT 0.0 .IP 1. 3 On the PKI: .UNINDENT .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 Create a pkcs#12 (\fB\&.p12\fP) file, rolling server key, server, private CA, sub CA certs into a single file. .IP \(bu 2 Use a password on the server key you won’t be ashamed of. .IP \(bu 2 Also create a single \fB\&.pem\fP file for the private CA + sub\-CA .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .IP 2. 3 On the hawkBit host: .UNINDENT .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 hawkBit uses the Java KeyStore to access credentials, but a JKS is not designed apparently to hold CA certs, which is a problem for private CAs. The workaround is to make it gulp an entire pkcs#12 file. .IP \(bu 2 It looks like a JKS like this cannot have a password different from the one protecting the \fB\&.p12\fP\&. Keytool also seems to have a little tendency to destruct the \fB\&.jks\fP if you change your mind and want to change the password… Basically do everything you need with openssl and use only keytool for generating the \fB\&.jks\fP file. .UNINDENT .sp The following command imports a \fB\&.p12\fP into a “pkcs12 Java keystore”, keeping the same password: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C keytool \-importkeystore \-srckeystore hb\-pass.p12 \-srcstoretype pkcs12 \e \-destkeystore hb\-pass.jks \-deststoretype pkcs12 \e \-alias 1 \-deststorepass .ft P .fi .UNINDENT .UNINDENT .sp Then you need to adapt \fBapplication.properties\fP of the hawkBit server to make use of the keystore. There are extra requirements to make hawkBit send artifacts via HTTPS. .sp This is the relevant part of \fB/hawkbit\-runtime/hawkbit\-update\-server/src/main/resources/application.properties\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # HTTPS mode working w/ swupdate # See also https://docs.spring.io/spring\-boot/docs/1.4.7.RELEASE/reference/html/howto\-embedded\-servlet\-containers.html#howto\-configure\-ssl # https://github.com/eclipse/hawkbit/issues/618 # # Need to run as root to use port 443 server.hostname=hb.domain server.port=8443 # # Overriding some of hawkbit\-artifactdl\-defaults.properties is required hawkbit.artifact.url.protocols.download\-http.protocol=https hawkbit.artifact.url.protocols.download\-http.port=8443 # # Upgrades http:8443 to https:8443 # Would redirect + upgrade http:80 to https:443 security.require\-ssl=true server.use\-forward\-headers=true # # Server cert+key w/ private CA + subCA # See also https://stackoverflow.com/questions/906402/how\-to\-import\-an\-existing\-x509\-certificate\-and\-private\-key\-in\-java\-keystore\-to\-u # http://cunning.sharp.fm/2008/06/importing_private_keys_into_a.html (2008, still relevant!?) # # File .jks is a .p12 imported via keytool. Only one password supported, set from openssl. server.ssl.key\-store=hb\-pass.jks server.ssl.key\-password=password server.ssl.key\-store\-password=password\-yes_the_same_one \&... .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .IP 3. 3 On the swupdate client host(s): .UNINDENT .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 The client needs the private CA certificate(s) to authenticate the server. .IP \(bu 2 There is a setting in swupdate to specify the path to a single CA cert, not a directory. Beyond that, libcurl looks into \fB/etc/ssl/certs\fP\&. So we’re using a compound “CA chain” \fB\&.pem\fP file to hold both private CA and sub\-CA in our preferred location. .UNINDENT .sp This is the relevant part of \fB/etc/swupdate/swupdate.conf\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&... suricatta : { tenant = \(dqdefault\(dq; id = \(dqmachineID\(dq; url = \(dqhttps://hb.domain:8443\(dq; nocheckcert = false; cafile = \(dq/etc/swupdate/priv\-cachain.pem\(dq; /* CA + sub CA in one file */ /* sslkey = anon client: do not set; */ /* sslcert = anon client: do not set; */ \&... .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SH SWUPDATE: API FOR EXTERNAL PROGRAMS .SS Overview .sp SWUpdate contains an integrated web\-server to allow remote updating. However, which protocols are involved during an update is project specific and differs significantly. Some projects can decide to use FTP to load an image from an external server, or using even a proprietary protocol. The integrated web\-server uses this interface. .sp SWUpdate has a simple interface to let external programs to communicate with the installer. Clients can start an upgrade and stream an image to the installer, querying then for the status and the final result. The API is at the moment very simple, but it can easy be extended in the future if new use cases will arise. .SS API Description .sp The communication runs via UDS (Unix Domain Socket). The socket is created at startup by SWUpdate in /tmp/sockinstctrl as per default configuration. This socket should, however, not be used directly but instead by the Client Library explained below. .sp The exchanged packets are described in network_ipc.h .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C typedef struct { int magic; int type; msgdata data; } ipc_message; .ft P .fi .UNINDENT .UNINDENT .sp Where the fields have the meaning: .INDENT 0.0 .IP \(bu 2 magic : a magic number as simple proof of the packet .IP \(bu 2 type : one of REQ_INSTALL, ACK, NACK, GET_STATUS, POST_UPDATE, SWUPDATE_SUBPROCESS, SET_AES_KEY .IP \(bu 2 msgdata : a buffer used by the client to send the image or by SWUpdate to report back notifications and status. .UNINDENT .sp The client sends a REQ_INSTALL packet and waits for an answer. SWUpdate sends back ACK or NACK, if for example an update is already in progress. .sp After the ACK, the client sends the whole image as a stream. SWUpdate expects that all bytes after the ACK are part of the image to be installed. SWUpdate recognizes the size of the image from the CPIO header. Any error lets SWUpdate to leave the update state, and further packets will be ignored until a new REQ_INSTALL will be received. [image] .sp It is recommended to use the client library to communicate with SWUpdate. On the lower level with direct socket communication, it cannot be guaranteed that the structures will remain compatible in the future. The client library was affected by this issue, too, and it is changed to accept an opaque interface that will survive API changes. Compatibility layers could be added on\-demand in the future due to API changes. .SS Client Library .SS Functions to start an update .sp A library simplifies the usage of the IPC making available a way to start asynchronously an update. .sp The library consists of one function and several call\-backs. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int swupdate_async_start(writedata wr_func, getstatus status_func, terminated end_func, void *req, ssize_t size) typedef int (*writedata)(char **buf, int *size); typedef int (*getstatus)(ipc_message *msg); typedef int (*terminated)(RECOVERY_STATUS status); .ft P .fi .UNINDENT .UNINDENT .sp swupdate_async_start creates a new thread and start the communication with SWUpdate, triggering for a new update. The wr_func is called to get the image to be installed. It is responsibility of the callback to provide the buffer and the size of the chunk of data. .sp The getstatus call\-back is called after the stream was downloaded to check how upgrade is going on. It can be omitted if only the result is required. .sp The terminated call\-back is called when SWUpdate has finished with the result of the upgrade. .sp Example about using this library is in the examples/client directory. .sp The \fIreq\fP structure is casted to void to ensure API compatibility. Am user should instantiate it as \fIstruct swupdate_request\fP\&. This contains fields that can control the update process: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C struct swupdate_request { unsigned int apiversion; sourcetype source; int dry_run; size_t len; char info[512]; char software_set[256]; char running_mode[256]; }; .ft P .fi .UNINDENT .UNINDENT .sp A user should first call \fIswupdate_prepare_req()\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C void swupdate_prepare_req(struct swupdate_request *req); .ft P .fi .UNINDENT .UNINDENT .sp This fills the request structure with default values. After that, the user can fill the other fields as: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 \fIsourcetype\fP : one of SOURCE_UNKNOWN, SOURCE_WEBSERVER, SOURCE_SURICATTA, SOURCE_DOWNLOADER, SOURCE_LOCAL .IP \(bu 2 \fIdry_run\fP : one of RUN_DEFAULT (set from command line), RUN_DRYRUN, RUN_INSTALL. .IP \(bu 2 \fIinfo, len\fP : a variable length data that can be forwarded to the progress interface. The installer in SWUpdate does not evaluate it. .IP \(bu 2 \fIsoftware_set\fP and \fIrunning_mode\fP : this allows one to set the \fIselection\fP for the update. .UNINDENT .UNINDENT .UNINDENT .SS Functions to set AES keys .sp The key for decryption can be set with command line parameter (see \fI\-K\fP), but it is possible to set it via IPC. In this way, each update could have a different key. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int swupdate_set_aes(char *key, char *ivt) .ft P .fi .UNINDENT .UNINDENT .sp The key is for AES\-256. The length for key and ivt are then defined by the algorithm amd they are passed as ASCII string, so the length \fImust\fP be 64 bytes for key and 32 bytes for IVT. .SS Functions to control SWUpdate .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int ipc_send_cmd(ipc_message *msg); .ft P .fi .UNINDENT .UNINDENT .sp ipc_send_cmd is used to send a command to a SWUpdate subprocess (as suricatta). The function is synchron, that means it clocks until the subprocess has answered with ACK or NACK. This function sets \fItype\fP to SWUPDATE_SUBPROCESS. The caller must then set the other fields in message according to the destination. The msgdata field is a structure as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C struct { sourcetype source; /* Who triggered the update */ int cmd; /* Optional encoded command */ int timeout; /* timeout in seconds if an aswer is expected */ unsigned int len; /* Len of data valid in buf */ char buf[2048]; /* * Buffer that each source can fill * with additional information */ } .ft P .fi .UNINDENT .UNINDENT .sp The caller fills \fIsource\fP with the subprocess that acceps the command. Values of cmd are in \fInetwork_ipc.h\fP\&. .SS Messages for suricatta .sp suricatta accepts messages in JSON format. The message must be formatted in the \fIbuf\fP field of the message data. .SS Setting the polling time .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { \(dqpolling\(dq : } .ft P .fi .UNINDENT .UNINDENT .sp Setting it to 0 has the special meaning that the polling time is retrieved from the Backend (if this is supported by the server). .SS Enable / disable Suricatta daemon .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { \(dqenable\(dq : true } { \(dqenable\(dq : false } .ft P .fi .UNINDENT .UNINDENT .SS Set custom device attributes for Suricatta (for Hawkbit implementation) .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { \(dqidentify\(dq : [ { \(dqname\(dq : \(dqcustomizableAttributeOne\(dq, \(dqvalue\(dq : \(dqvalueOne\(dq }, { \(dqname\(dq : \(dqcustomizableAttributeTwo\(dq, \(dqvalue\(dq : \(dqvalueTwo\(dq } ]} .ft P .fi .UNINDENT .UNINDENT .sp New attributes can be added at runtime, and existing attributes can be modified in the same way. Changes will be reflected on the server in the next poll iteration. .SS Trigger a check on the server .sp This is useful in case the device is mostly offline, and when it is online, it should check immediately if an update exists and run it. In fact, after enabling the suricatta daemon, the update follows the usual states, and the daemon waits for a polling time before loading the new software. This command forces an update (if available) without changing the polling time. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { \(dqtrigger\(dq : true } .ft P .fi .UNINDENT .UNINDENT .SS Activate an already installed Software .sp After a software was installed, the new software boots and if everything runs fine, an acknowledge should be sent to the hawkBit server. If this feature is used, for example to let the end user decide if the new software is accepted, the parameters used by the installation should be stored during the update process. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { \(dqid\(dq : , \(dqfinished\(dq : \(dqsuccess\(dq, \(dqfailure\(dq, \(dqnone\(dq, \(dqexecution\(dq : [\(dqclosed\(dq, \(dqproceeding\(dq, canceled\(dq, \(dqrejected\(dq, \(dqresumed\(dq] \(dqdetails\(dq : [ ] } .ft P .fi .UNINDENT .UNINDENT .SS Get hawkBit Server Status .sp To provide the hawkBit server status to other processes, it can be requested by sending an empty message with message type CMD_GET_STATUS. .sp The response is a JSON object containing the hawkBit server status . is a number representing the value of the channel_op_res_t enum from channel_op_res.h. As the hawkBit server is polled, its status can only be updated when it has been polled. Therefore the response also contains the time