summaryrefslogtreecommitdiffstats
path: root/external/meta-updater
diff options
context:
space:
mode:
Diffstat (limited to 'external/meta-updater')
-rw-r--r--external/meta-updater/.gitignore2
-rw-r--r--external/meta-updater/.gitlab-ci.yml91
-rw-r--r--external/meta-updater/CONTRIBUTING.adoc34
-rw-r--r--external/meta-updater/COPYING.MIT17
-rw-r--r--external/meta-updater/README.adoc279
-rw-r--r--external/meta-updater/classes/image_repo_manifest.bbclass23
-rw-r--r--external/meta-updater/classes/image_types_ostree.bbclass266
-rw-r--r--external/meta-updater/classes/image_types_ota.bbclass113
-rw-r--r--external/meta-updater/classes/sota.bbclass66
-rw-r--r--external/meta-updater/classes/sota_am335x-evm-wifi.bbclass15
-rw-r--r--external/meta-updater/classes/sota_bleeding.inc1
-rw-r--r--external/meta-updater/classes/sota_m3ulcb.bbclass11
-rw-r--r--external/meta-updater/classes/sota_minnowboard.bbclass12
-rw-r--r--external/meta-updater/classes/sota_none.bbclass4
-rw-r--r--external/meta-updater/classes/sota_porter.bbclass11
-rw-r--r--external/meta-updater/classes/sota_qemux86-64.bbclass16
-rw-r--r--external/meta-updater/classes/sota_raspberrypi.bbclass42
-rw-r--r--external/meta-updater/classes/sota_sanity.bbclass54
-rw-r--r--external/meta-updater/classes/target_version_example.bbclass10
-rw-r--r--external/meta-updater/conf/distro/poky-sota-systemd.conf12
-rw-r--r--external/meta-updater/conf/distro/poky-sota.conf7
-rw-r--r--external/meta-updater/conf/distro/sota.conf.inc19
-rw-r--r--external/meta-updater/conf/include/bblayers/sota.inc3
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_am335x-evm.inc1
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_intel-corei7-64.inc2
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_m3ulcb.inc3
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_porter.inc4
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_qemux86-64.inc1
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_raspberrypi2.inc3
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_raspberrypi3-64.inc2
-rw-r--r--external/meta-updater/conf/include/bblayers/sota_raspberrypi3.inc4
-rw-r--r--external/meta-updater/conf/layer.conf13
-rw-r--r--external/meta-updater/conf/local.conf.sample.append35
l---------external/meta-updater/lib/oeqa/selftest/cases/qemucommand.py1
-rw-r--r--external/meta-updater/lib/oeqa/selftest/cases/testutils.py143
-rw-r--r--external/meta-updater/lib/oeqa/selftest/cases/updater_minnowboard.py54
-rw-r--r--external/meta-updater/lib/oeqa/selftest/cases/updater_native.py48
-rw-r--r--external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64.py476
-rw-r--r--external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64_ptest.py47
-rw-r--r--external/meta-updater/lib/oeqa/selftest/cases/updater_raspberrypi.py82
-rw-r--r--external/meta-updater/recipes-connectivity/connman/connman_%.bbappend1
-rw-r--r--external/meta-updater/recipes-connectivity/networkd-dhcp-conf/files/20-wired-dhcp.network5
-rw-r--r--external/meta-updater/recipes-connectivity/networkd-dhcp-conf/networkd-dhcp-conf.bb28
-rw-r--r--external/meta-updater/recipes-core/images/initramfs-ostree-image.bb29
-rw-r--r--external/meta-updater/recipes-devtools/valgrind/files/bug344802-unhandled-0xec510f1e.patch250
-rw-r--r--external/meta-updater/recipes-devtools/valgrind/valgrind_%.bbappend4
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-creds.bb60
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-hsm.bb30
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov.bb29
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov-creds.bb32
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov.bb43
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr-uboot-env-rollback.bb24
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/aktualizr_git.bb171
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/credentials.inc1
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/files/10-resource-control.conf6
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/files/aktualizr-secondary.service12
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/files/aktualizr-serialcan.service15
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/files/aktualizr.service11
-rw-r--r--external/meta-updater/recipes-sota/aktualizr/files/ca.cnf10
-rwxr-xr-xexternal/meta-updater/recipes-sota/aktualizr/files/run-ptest18
-rw-r--r--external/meta-updater/recipes-sota/asn1c/asn1c.bb17
-rw-r--r--external/meta-updater/recipes-sota/asn1c/files/skeletons_dir_fix.patch44
-rw-r--r--external/meta-updater/recipes-sota/config/aktualizr-auto-reboot.bb23
-rw-r--r--external/meta-updater/recipes-sota/config/aktualizr-disable-send-ip.bb24
-rw-r--r--external/meta-updater/recipes-sota/config/aktualizr-log-debug.bb24
-rw-r--r--external/meta-updater/recipes-sota/config/aktualizr-polling-interval.bb29
-rw-r--r--external/meta-updater/recipes-sota/config/files/05-log-debug.toml2
-rw-r--r--external/meta-updater/recipes-sota/config/files/30-disable-send-ip.toml2
-rw-r--r--external/meta-updater/recipes-sota/config/files/35-enable-auto-reboot.toml2
-rw-r--r--external/meta-updater/recipes-sota/config/files/60-polling-interval.toml2
-rw-r--r--external/meta-updater/recipes-sota/fit-conf/fit-conf.bb24
-rw-r--r--external/meta-updater/recipes-sota/ostree-initrd/files/init.sh71
-rw-r--r--external/meta-updater/recipes-sota/ostree-initrd/ostree-initrd.bb20
-rw-r--r--external/meta-updater/recipes-sota/ostree/ostree_git.bb69
-rw-r--r--external/meta-updater/recipes-support/fuse/fuse_%.bbappend3
-rw-r--r--external/meta-updater/recipes-support/gpgme/gpgme_%.bbappend1
-rw-r--r--external/meta-updater/recipes-support/libp11/libp11_git.bb40
-rw-r--r--external/meta-updater/recipes-support/libssh2/libssh2_%.bbappend2
-rw-r--r--external/meta-updater/recipes-support/lshw/files/cross-compile.patch39
-rw-r--r--external/meta-updater/recipes-support/lshw/files/ldflags.patch42
-rw-r--r--external/meta-updater/recipes-support/lshw/lshw_02.17.bb36
-rw-r--r--external/meta-updater/recipes-support/slcand-start/files/slcand@.service8
-rw-r--r--external/meta-updater/recipes-support/slcand-start/slcand-start.bb21
-rw-r--r--external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.service12
-rw-r--r--external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.sh27
-rw-r--r--external/meta-updater/recipes-support/softhsm-testtoken/softhsm-testtoken.bb25
-rw-r--r--external/meta-updater/recipes-support/softhsm/softhsm_git.bb26
-rw-r--r--external/meta-updater/recipes-support/systemd-journald-persistent/files/10-persistent-journal.conf3
-rw-r--r--external/meta-updater/recipes-support/systemd-journald-persistent/systemd-journald-persistent.bb20
-rw-r--r--external/meta-updater/recipes-test/big-update/big-update_1.0.bb13
-rw-r--r--external/meta-updater/recipes-test/big-update/big-update_2.0.bb13
-rw-r--r--external/meta-updater/recipes-test/big-update/files/rand_file.py16
-rw-r--r--external/meta-updater/recipes-test/demo-config/files/30-fake-pacman.toml2
-rw-r--r--external/meta-updater/recipes-test/demo-config/files/30-secondary-config.toml2
-rw-r--r--external/meta-updater/recipes-test/demo-config/files/35-network-config.toml4
-rw-r--r--external/meta-updater/recipes-test/demo-config/files/45-id-config.toml3
-rw-r--r--external/meta-updater/recipes-test/demo-config/files/ip_secondary_config.json7
-rw-r--r--external/meta-updater/recipes-test/demo-config/primary-config.bb68
-rw-r--r--external/meta-updater/recipes-test/demo-config/secondary-config.bb41
-rw-r--r--external/meta-updater/recipes-test/demo-config/shared-conf.inc5
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/files/25-dhcp-server.network12
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/files/26-dhcp-client.network6
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/files/26-multihomed-client.network9
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/files/26-static-client.network7
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/files/27-dhcp-client-external.network6
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/network-config.inc17
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/primary-network-config.bb27
-rw-r--r--external/meta-updater/recipes-test/demo-network-config/secondary-network-config.bb30
-rw-r--r--external/meta-updater/recipes-test/images/primary-image.bb16
-rw-r--r--external/meta-updater/recipes-test/images/secondary-image.bb29
-rw-r--r--external/meta-updater/scripts/ci/Dockerfile.bitbake42
-rw-r--r--external/meta-updater/scripts/ci/Jenkinsfile.bleeding87
-rw-r--r--external/meta-updater/scripts/ci/Jenkinsfile.bleeding-selftest91
-rw-r--r--external/meta-updater/scripts/ci/README.adoc14
-rwxr-xr-xexternal/meta-updater/scripts/ci/build.sh18
-rwxr-xr-xexternal/meta-updater/scripts/ci/configure.sh65
-rwxr-xr-xexternal/meta-updater/scripts/ci/oe-selftest.sh18
-rwxr-xr-xexternal/meta-updater/scripts/envsetup.sh36
-rwxr-xr-xexternal/meta-updater/scripts/find_aktualizr_dependencies.sh25
-rwxr-xr-xexternal/meta-updater/scripts/find_dependencies.py214
-rw-r--r--external/meta-updater/scripts/lib/wic/canned-wks/efiimage-sota.wks8
-rw-r--r--external/meta-updater/scripts/lib/wic/canned-wks/grub-ota.cfg2
-rw-r--r--external/meta-updater/scripts/lib/wic/canned-wks/sdimage-sota.wks7
-rw-r--r--external/meta-updater/scripts/lib/wic/plugins/source/otaimage.py69
-rwxr-xr-xexternal/meta-updater/scripts/qa60
-rw-r--r--external/meta-updater/scripts/qemucommand.py137
-rwxr-xr-xexternal/meta-updater/scripts/run-qemu-ota74
127 files changed, 4764 insertions, 0 deletions
diff --git a/external/meta-updater/.gitignore b/external/meta-updater/.gitignore
new file mode 100644
index 00000000..8d35cb32
--- /dev/null
+++ b/external/meta-updater/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+*.pyc
diff --git a/external/meta-updater/.gitlab-ci.yml b/external/meta-updater/.gitlab-ci.yml
new file mode 100644
index 00000000..6ad00ea3
--- /dev/null
+++ b/external/meta-updater/.gitlab-ci.yml
@@ -0,0 +1,91 @@
+stages:
+ - docker
+ - checkout
+ - test
+
+variables:
+ BITBAKE_IMAGE: ${CI_REGISTRY_IMAGE}:ci-master-bitbake
+ BITBAKE_CHECKOUT_IMAGE: ${CI_REGISTRY_IMAGE}:ci-master-checkout
+
+include:
+ - project: 'olp/edge/ota/connect/client/meta-updater'
+ ref: 'master'
+ file: 'scripts/ci/gitlab/docker.yml'
+ - project: 'olp/edge/ota/connect/client/meta-updater'
+ ref: 'master'
+ file: 'scripts/ci/gitlab/checkout.yml'
+ - project: 'olp/edge/ota/connect/client/meta-updater'
+ ref: 'master'
+ file: 'scripts/ci/gitlab/tests.yml'
+
+Docker setup:
+ extends: .bb_docker_remote
+
+ stage: docker
+ except:
+ - pushes
+
+Checkout:
+ extends: .bb_checkout
+
+ stage: checkout
+ variables:
+ MANIFEST: thud
+ CURRENT_PROJECT: meta-updater
+ except:
+ - pushes
+
+Build core-image-minimal:
+ extends: .bitbake
+
+ stage: test
+ variables:
+ TEST_BUILD_DIR: 'build-core-image-minimal'
+ BITBAKE_TARGETS: 'core-image-minimal'
+ except:
+ - pushes
+
+Oe-selftest qemux86_64:
+ extends: .oe-selftest
+
+ stage: test
+ variables:
+ TEST_BUILD_DIR: 'build-oe-qemux86_64'
+ OE_SELFTESTS: 'updater_native updater_qemux86_64'
+ except:
+ - pushes
+
+Oe-selftest minnowboard:
+ extends: .oe-selftest
+
+ stage: test
+ variables:
+ TEST_BUILD_DIR: 'build-oe-minnowboard'
+ OE_SELFTESTS: 'updater_minnowboard'
+ except:
+ - pushes
+
+Oe-selftest rpi:
+ extends: .oe-selftest
+
+ stage: test
+ variables:
+ TEST_BUILD_DIR: 'build-oe-rpi'
+ OE_SELFTESTS: 'updater_raspberrypi'
+ except:
+ - pushes
+
+# Not run by default
+
+Ptest qemux86_64:
+ extends: .oe-selftest
+
+ stage: test
+ variables:
+ TEST_BUILD_DIR: 'build-oe-qemux86_64-ptest'
+ OE_SELFTESTS: 'updater_qemux86_64_ptest'
+ except:
+ - pushes
+ only:
+ variables:
+ - $OE_PTEST
diff --git a/external/meta-updater/CONTRIBUTING.adoc b/external/meta-updater/CONTRIBUTING.adoc
new file mode 100644
index 00000000..0b404382
--- /dev/null
+++ b/external/meta-updater/CONTRIBUTING.adoc
@@ -0,0 +1,34 @@
+= Contributing
+
+We welcome pull requests from anyone. The master branch is the primary branch for development, and if you wish to add new functionality, it probably belongs there. We attempt to maintain recent previous branches and welcome bug fixes and backports for those. Currently, the actively maintained branches are:
+
+* thud
+* sumo
+* rocko
+
+Previously, some older branches were also regularly supported, and while they should still be stable, they have not been updated or actively maintained for a while. These branches include:
+
+* pyro
+* morty
+
+If you are developing with meta-updater, it may be helpful to read the README and other documentation for link:README.adoc[this repo], https://github.com/advancedtelematic/aktualizr[aktualizr], and the https://github.com/advancedtelematic/updater-repo/[updater-repo], particularly the sections about development and debugging.
+
+== Developer Certificate of Origin (DCO)
+
+All commits in pull requests must contain a `Signed-off-by:` line to indicate that the developer has agreed to the terms of the https://developercertificate.org[Developer Certificate of Origin]. A simple way to achieve that is to use the `-s` flag of `git commit`.
+
+New pull requests will automatically be checked by the https://probot.github.io/apps/dco/[probot/dco].
+
+== Contributor checklist
+
+* OTA-enabled build succeeds for at least one platform, the resulting image boots, and an update can be installed. This check is absolutely necessary for every pull request unless it only touches documentation.
+* If your change touches platform code (like `classes/sota_<platform>.bbclass`), please check building and updating on this particular platform.
+* oe-selftest succeeds. To test meta-updater, run `oe-selftest -r updater` from a build directory with `MACHINE` set to `qemux86-64`. See the link:README.adoc#qa-with-oe-selftest[relevant section of the README] for more details.
+* Updates are forwards- and backwards-compatible. You should be able to update an OTA-enabled build before the change is applied to the version with change applied and vice versa. One should pay double attention to the compatibility when bootloader code is affected.
+* The patch/branch should be based on the latest version of the target branch. This may mean that rebasing is necessary if other PRs are merged before yours is approved.
+
+We understand that completing all these tasks might be overly tedious due to build times in Yocto. Please add a comment to your PR describing the tests you've done.
+
+== Approval
+
+PR approval should be accompanied by a comment describing what tests have been done by the reviewer.
diff --git a/external/meta-updater/COPYING.MIT b/external/meta-updater/COPYING.MIT
new file mode 100644
index 00000000..fb950dc6
--- /dev/null
+++ b/external/meta-updater/COPYING.MIT
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/external/meta-updater/README.adoc b/external/meta-updater/README.adoc
new file mode 100644
index 00000000..b047f914
--- /dev/null
+++ b/external/meta-updater/README.adoc
@@ -0,0 +1,279 @@
+= meta-updater
+:toc: macro
+:toc-title:
+
+This layer enables over-the-air updates (OTA) with https://github.com/ostreedev/ostree[OSTree] and https://github.com/advancedtelematic/aktualizr[Aktualizr].
+
+https://github.com/ostreedev/ostree[OSTree] is a tool for atomic full file system upgrades with rollback capability. OSTree has several advantages over traditional dual-bank systems, but the most important one is that it minimizes network bandwidth and data storage footprint by sharing files with the same contents across file system deployments.
+
+https://github.com/advancedtelematic/aktualizr[Aktualizr] (and https://github.com/advancedtelematic/rvi_sota_client[RVI SOTA client]) add authentication and provisioning capabilities to OTA and are integrated with OSTree. You can connect with these open-source applications or sign up for a free account at https://connect.ota.here.com/[HERE OTA Connect] to get started.
+
+[discrete]
+== Table of Contents
+
+toc::[]
+
+== Build
+
+=== Quickstart
+
+If you don't already have a Yocto project that you want to add OTA to, you can use the https://docs.atsgarage.com/quickstarts/raspberry-pi.html[HERE OTA Connect Quickstart] project to rapidly get up and running on a Raspberry Pi. It takes a standard https://www.yoctoproject.org/tools-resources/projects/poky[poky] distribution, and adds OTA and OSTree capabilities.
+
+=== Dependencies
+
+In addition to the link:https://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#required-packages-for-the-build-host[standard Yocto dependencies], meta-updater generally requires a few additional dependencies, depending on your use case and target platform. To install these additional packages on Debian/Ubuntu, run this:
+
+....
+sudo apt install cpu-checker default-jre parted
+....
+
+To build for https://github.com/advancedtelematic/meta-updater-minnowboard[Minnowboard] with GRUB, you will also need to install https://github.com/tianocore/tianocore.github.io/wiki/OVMF[TianoCore's ovmf] package on your host system. On Debian/Ubuntu, you can do so with this command:
+
+....
+sudo apt install ovmf
+....
+
+=== Adding meta-updater capabilities to your build
+
+If you already have a Yocto-based project and you want to add atomic filesystem updates to it, you just need to do three things:
+
+1. Clone the `meta-updater` layer and add it to your https://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#structure-build-conf-bblayers.conf[bblayers.conf].
+2. Clone BSP integration layer (`meta-updater-$\{PLATFORM}`, e.g. https://github.com/advancedtelematic/meta-updater-raspberrypi[meta-updater-raspberrypi]) and add it to your `conf/bblayers.conf`. If your board isn't supported yet, you could write a BSP integration for it yourself. See the <<Adding support for your board>> section for the details.
+3. Set up your https://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-DISTRO[distro]. If you are using "poky", the default distro in Yocto, you can change it in your `conf/local.conf` to "poky-sota". Alternatively, if you are using your own or third party distro configuration, you can add `INHERIT += " sota"` to it, thus combining capabilities of your distro with meta-updater features.
+
+You can then build your image as usual, with bitbake. After building the root file system, bitbake will then create an https://ostree.readthedocs.io/en/latest/manual/adapting-existing/[OSTree-enabled version] of it, commit it to your local OSTree repo and (optionally) push it to a remote server. Additionally, a live disk image will be created (normally named `$\{IMAGE_NAME}.-sdimg-ota` e.g. `core-image-raspberrypi3.rpi-sdimg-ota`). You can control this behaviour through <<sota-related-variables-in-localconf,variables in your local.conf>>.
+
+=== Build in AGL
+
+With AGL you can just add agl-sota feature while configuring your build environment:
+
+....
+source meta-agl/scripts/aglsetup.sh -m porter agl-demo agl-appfw-smack agl-devel agl-sota
+....
+
+You can then run:
+
+....
+bitbake agl-demo-platform
+....
+
+and get as a result an `ostree_repo` folder in your images directory (`tmp/deploy/images/$\{MACHINE}/ostree_repo`). It will contain:
+
+* your OSTree repository, with the rootfs committed as an OSTree deployment,
+* an `ota-ext4` bootstrap image, which is an OSTree physical sysroot as a burnable filesystem image, and optionally
+* some machine-dependent live images (e.g. `.wic` for Raspberry Pi or `.porter-sdimg-ota` Renesas Porter board).
+
+Although `aglsetup.sh` hooks provide reasonable defaults for SOTA-related variables, you may want to tune some of them.
+
+=== Build problems
+
+Ubuntu users that encounter an error due to missing `Python.h` should install `libpython2.7-dev` on their host machine.
+
+== Supported boards
+
+Currently supported platforms are
+
+* https://github.com/advancedtelematic/meta-updater-raspberrypi[Raspberry Pi3]
+* https://github.com/advancedtelematic/meta-updater-minnowboard[Minnowboard]
+* https://github.com/advancedtelematic/meta-updater-qemux86-64[Native QEMU emulation]
+
+=== Adding support for your board
+
+If your board isn't supported yet, you can add board integration code yourself. The main purpose of this code is to provide a bootloader that will be able to use https://ostree.readthedocs.io/en/latest/manual/atomic-upgrades/[OSTree's boot directory]. In the meta-updater integration layers we have written so far, the basic steps are:
+
+1. Make the board boot into http://www.denx.de/wiki/U-Boot[U-Boot]
+2. Make U-boot import variables from /boot/loader/uEnv.txt and load the kernel with initramfs and kernel command line arguments according to what is set in this file.
+
+You may take a look into https://github.com/advancedtelematic/meta-updater-minnowboard[Minnowboard] or https://github.com/advancedtelematic/meta-updater-raspberrypi[Raspberry Pi] integration layers for examples.
+
+Although we have focused on U-Boot and GRUB so far, other bootloaders can be configured to work with OSTree as well.
+
+Your images will also need network connectivity to be able to reach an actual OTA backend. Our 'poky-sota' distribution does not mandate or install a default network manager but our supported platforms use the `virtual/network-configuration` recipe, which can be used as a starting example.
+
+== SOTA-related variables in local.conf
+
+* `OSTREE_REPO` - path to your OSTree repository. Defaults to `$\{DEPLOY_DIR_IMAGE}/ostree_repo`
+* `OSTREE_OSNAME` - OS deployment name on your target device. For more information about deployments and osnames see the https://ostree.readthedocs.io/en/latest/manual/deployment/[OSTree documentation]. Defaults to "poky".
+* `OSTREE_COMMIT_BODY` - Message attached to OSTree commit. Empty by default.
+* `OSTREE_COMMIT_SUBJECT` - Commit subject used by OSTree. Defaults to `Commit-id: ${IMAGE_NAME}`
+* `OSTREE_UPDATE_SUMMARY` - Set this to '1' to update summary of OSTree repository on each commit. '0' by default.
+* `OSTREE_DEPLOY_DEVICETREE` - Set this to '1' to include devicetree(s) to boot
+* `GARAGE_SIGN_AUTOVERSION` - Set this to '1' to automatically fetch the last version of the garage tools installed by the aktualizr-native. Otherwise use the fixed version specified in the recipe.
+* `INITRAMFS_IMAGE` - initramfs/initrd image that is used as a proxy while booting into OSTree deployment. Do not change this setting unless you are sure that your initramfs can serve as such a proxy.
+* `SOTA_PACKED_CREDENTIALS` - when set, your ostree commit will be pushed to a remote repo as a bitbake step. This should be the path to a zipped credentials file in https://github.com/advancedtelematic/aktualizr/blob/master/docs/credentials.adoc[the format accepted by garage-push].
+* `SOTA_DEPLOY_CREDENTIALS` - when set to '1' (default value), deploys credentials to the built image. Override it in `local.conf` to built a generic image that can be provisioned manually after the build.
+* `SOTA_CLIENT_PROV` - which provisioning method to use. Valid options are `aktualizr-shared-prov`, `aktualizr-device-prov`, and `aktualizr-device-prov-hsm`. For more information on these provisioning methods, see the https://docs.ota.here.com/client-config/client-provisioning-methods.html[OTA Connect documentation]. The default is `aktualizr-shared-prov`. This can also be set to an empty string to avoid using a provisioning recipe.
+* `SOTA_CLIENT_FEATURES` - extensions to aktualizr. The only valid options are `hsm` (to build with HSM support) and `secondary-network` (to set up a simulated 'in-vehicle' network with support for a primary node with a DHCP server and a secondary node with a DHCP client).
+* `SOTA_SECONDARY_CONFIG` - a file containing JSON configuration for secondaries. It will be installed into `/etc/sota/ecus` on the device and automatically provided to aktualizr. See link:https://github.com/advancedtelematic/aktualizr/blob/master/docs/posix-secondaries-bitbaking.adoc[here] for more details.
+* `SOTA_HARDWARE_ID` - a custom hardware ID that will be written to the aktualizr config. Defaults to MACHINE if not set.
+* `SOTA_MAIN_DTB` - base device tree to use with the kernel. Used together with FIT images. You can change it, and the device tree will also be changed after the update.
+* `SOTA_DT_OVERLAYS` - whitespace-separated list of used device tree overlays for FIT image. This list is OSTree-updateable as well.
+* `SOTA_EXTRA_CONF_FRAGS` - extra https://lxr.missinglinkelectronics.com/uboot/doc/uImage.FIT/overlay-fdt-boot.txt[configuration fragments] for FIT image.
+* `RESOURCE_xxx_pn-aktualizr` - controls maximum resource usage of the aktualizr service, when `aktualizr-resource-control` is installed on the image. See <<aktualizr service resource control>> for details.
+* `SOTA_POLLING_SEC` - sets polling interval for aktualizr to check for updates if aktualizr-polling-sec is included in the image.
+
+== Usage
+
+=== OSTree
+
+OSTree used to include a simple HTTP server as part of the ostree binary, but this has been removed in more recent versions. However, OSTree repositories are self-contained directories, and can be trivially served over the network using any HTTP server. For example, you could use Python's SimpleHTTPServer:
+
+....
+cd tmp/deploy/images/qemux86-64/ostree_repo
+python -m SimpleHTTPServer <port> # port defaults to 8000
+....
+
+You can then run ostree from inside your device by adding your repo:
+
+....
+# This behaves like adding a Git remote; you can name it anything
+ostree remote add --no-gpg-verify my-remote http://<your-ip>:<port>
+
+# If OSTREE_BRANCHNAME is set in local.conf, that will be the name of the
+# branch. If not set, it defaults to the value of MACHINE (e.g. qemux86-64).
+ostree pull my-remote <branch>
+
+# poky is the OS name as set in OSTREE_OSNAME
+ostree admin deploy --os=poky my-remote:<branch>
+....
+
+After restarting, you will boot into the newly deployed OS image.
+
+For example, on the raspberry pi you can try this sequence:
+
+....
+# add remote
+ostree remote add --no-gpg-verify agl-snapshot https://download.automotivelinux.org/AGL/snapshots/master/latest/raspberrypi3/deploy/images/raspberrypi3/ostree_repo/ agl-ota
+
+# pull
+ostree pull agl-snapshot agl-ota
+
+# deploy
+ostree admin deploy --os=agl agl-snapshot:agl-ota
+....
+
+=== garage-push
+
+The https://github.com/advancedtelematic/aktualizr[aktualizr repo] contains a tool, garage-push, which lets you push the changes in OSTree repository generated by bitbake process. It communicates with an http server capable of querying files with HEAD requests and uploading them with POST requests. In particular, this can be used with https://connect.ota.here.com/[HERE OTA Connect]. garage-push is used as follows:
+
+....
+garage-push --repo=/path/to/ostree-repo --ref=mybranch --credentials=/path/to/credentials.zip
+....
+
+You can set `SOTA_PACKED_CREDENTIALS` in your `local.conf` to automatically synchronize your build results with a remote server. Credentials are stored in an archive as described in the https://github.com/advancedtelematic/aktualizr/blob/master/docs/credentials.adoc[aktualizr documentation].
+
+=== aktualizr configuration
+
+https://github.com/advancedtelematic/aktualizr[Aktualizr] supports a variety of https://github.com/advancedtelematic/aktualizr/blob/master/docs/configuration.adoc[configuration options via a configuration file and the command line]. There are two primary ways to control aktualizr's configuration from meta-updater.
+
+First, you can set `SOTA_CLIENT_PROV` to control which provisioning recipe is used. Each recipe installs an appropriate `sota.toml` file from aktualizr according to the provisioning needs. See the <<sota-related-variables-in-localconf,SOTA-related variables in local.conf>> section for more information.
+
+Second, you can write recipes to install additional config files with customized options. A few recipes already exist to address common needs and provide an example:
+
+* link:recipes-sota/config/aktualizr-auto-reboot.bb[aktualizr-auto-reboot.bb] configures aktualizr to automatically reboot after new updates are installed in order to apply the updates immediately. This is only relevant for package managers (such as OSTree) that require a reboot to complete the installation process. If this is not enabled, you will need to reboot the system through other means.
+* link:recipes-sota/config/aktualizr-disable-send-ip.bb[aktualizr-disable-send-ip.bb] disables the reporting of networking information to the server. This is enabled by default and supported by https://connect.ota.here.com/[HERE OTA Connect]. However, if you are using a different server that does not support this feature, you may want to disable it in aktualizr.
+* link:recipes-sota/config/aktualizr-log-debug.bb[aktualizr-log-debug.bb] sets the log level of aktualizr to 0 (trace). The default is 2 (info). This recipe is intended for development and debugging purposes.
+
+To use these recipes, you will need to add them to your image with a line such as `IMAGE_INSTALL_append = " aktualizr-log-debug "` in your `local.conf`.
+
+=== aktualizr service resource control
+
+With systemd based images, it is possible to set resource policies for the aktualizr service. The main use case is to provide a safeguard against resource exhaustion during an unforeseen failure scenario.
+
+To enable it, install `aktualizr-resource-control` on the target image and optionally override the default resource limits set in link:recipes-sota/aktualizr/aktualizr_git.bb[aktualizr_git.bb], from your `local.conf`.
+
+For example:
+
+....
+IMAGE_INSTALL_append += " aktualizr-resource-control "
+RESOURCE_CPU_WEIGHT_pn-aktualizr = "50"
+....
+
+== Development configuration
+
+There are a few settings that can be controlled in `local.conf` to simplify the development process:
+
+[options="header"]
+|======================
+| Option | Effect
+| `require classes/sota_bleeding.inc` | Build the latest head (by default, using the master branch) of Aktualizr
+| `BRANCH_pn-aktualizr = "mybranch"`
+
+`BRANCH_pn-aktualizr-native = "mybranch"` | Build `mybranch` of Aktualizr. Note that both of these need to be set. This is normally used in conjunction with `require classes/sota_bleeding.inc`
+| `SRCREV_pn-aktualizr = "1004efa3f86cef90c012b34620992b5762b741e3"`
+
+`SRCREV_pn-aktualizr-native = "1004efa3f86cef90c012b34620992b5762b741e3"` | Build the specified revision of Aktualizr. Note that both of these need to be set. This can be used in conjunction with `BRANCH_pn-aktualizr` and `BRANCH_pn-aktualizr-native` but will conflict with `require classes/sota_bleeding.inc`
+| `TOOLCHAIN_HOST_TASK_append = " nativesdk-cmake "` | Use with `bitbake -c populate_sdk core-image-minimal` to build an SDK. See the https://github.com/advancedtelematic/aktualizr#developing-against-an-openembedded-system[aktualizr repo] for more information.
+|======================
+
+=== Overriding target version
+*Warning: overriding target version is a dangerous operation, make sure you understand this section completely before doing it.*
+
+Every time you build an image with `SOTA_PACKED_CREDENTIALS` set, a new entry in your Uptane metadata is created and you can see it in the OTA Garage UI if you're using one. Normally this version will be equal to OSTree hash of your root file system. If you want it to be different though you can override is using one of two methods:
+
+1. Set `GARAGE_TARGET_VERSION` variable in your `local.conf`.
+2. Write a recipe or a bbclass to write the desired version to `${STAGING_DATADIR_NATIVE}/target_version`. An example of such bbclass can be found in `classes/target_version_example.bbclass`.
+
+Please note that [target name, target version] pairs are expected to be unique in the system. If you build a new target with the same target version as a previously built one, the old package will be overwritten on the update server. It can have unpredictable effect on devices that have this version installed, and it is not guaranteed that information will be reported correctly for such devices or that you will be able to update them (we're doing our best though). The easiest way to avoid problems is to make sure that your overriding version is as unique as an OSTree commit hash.
+
+== QA with oe-selftest
+
+This layer relies on the test framework oe-selftest for quality assurance. Currently, you will need to run this in a build directory with `MACHINE` set to `qemux86-64`. Follow the steps below to run the tests:
+
+1. Append the line below to `conf/local.conf` to disable the warning about supported operating systems:
++
+```
+SANITY_TESTED_DISTROS = ""
+```
+
+2. If your image does not already include an ssh daemon such as dropbear or openssh, add this line to `conf/local.conf` as well:
++
+```
+IMAGE_INSTALL_append = " dropbear "
+```
+
+3. Some tests require that `SOTA_PACKED_CREDENTIALS` is set in your `conf/local.conf`. See the <<sota-related-variables-in-localconf,SOTA-related variables in local.conf>> section.
+
+4. To be able to build an image for the GRUB tests, you will need to install the ovmf package as described in the <<Dependencies,dependencies>>.
+
+5. Run oe-selftest:
++
+```
+oe-selftest -r updater_native updater_qemux86_64 updater_minnowboard updater_raspberrypi updater_qemux86_64_ptest
+```
+
+For more information about oe-selftest, including details about how to run individual test modules or classes, please refer to the https://wiki.yoctoproject.org/wiki/Oe-selftest[Yocto Project wiki].
+
+== Aktualizr test suite with ptest
+
+The meta-updater layer includes support for running parts of the aktualizr test suite on deployed devices through link:https://wiki.yoctoproject.org/wiki/Ptest[Yocto's ptest functionality]. Since it adds significant build time cost, it is currently disabled by default. To enable it, add the following to your `conf/local.conf`:
+
+```
+PTEST_ENABLED_pn-aktualizr = "1"
+IMAGE_INSTALL_append += " aktualizr-ptest ptest-runner "
+```
+
+Be aware that it will add several hundreds of MB to the generated file system.
+
+The aktualizr tests will now be part of the deployed ptest suite, which can be run by calling `ptest-runner`. Alternatively, the required files and run script can be found in `/usr/lib/aktualizr/ptest`.
+
+== Manual provisoning
+
+As described in <<sota-related-variables-in-localconf,SOTA-related variables in local.conf>> section you can set `SOTA_DEPLOY_CREDENTIALS` to `0` to prevent deploying credentials to the built `wic` image. In this case you get a generic image that you can use e.g. on a production line to flash a series of devices. The cost of this approach is that this image is half-baked and should be provisioned before it can connect to the backend.
+
+Provisioning procedure depends on your provisioning recipe, i.e. the value of `SOTA_CLIENT_PROV` (equal to `aktualizr-shared-prov` by default):
+
+* For `aktualizr-shared-prov` put your `credentials.zip` to `/var/sota/sota_provisioning_credentials.zip` on the filesystem of a running device. If you have the filesystem of our device mounted to your build machine, prefix all paths with `/ostree/deploy/poky` as in `/ostree/deploy/poky/var/sota/sota_provisioning_credentials.zip`.
+* For `aktualizr-device-prov`
+** put URL to the backend server (together with protocol prefix and port number) at `/var/sota/gateway.url`. If you're using HERE OTA Connect, you can find the URL in the `autoprov.url` file in your credentials archive.
+** put client certificate, private key and root CA certificate (for the *server*, not for the *device*) at `/var/sota/import/client.pem`, `/var/sota/import/pkey.pem` and `/var/sota/import/root.crt` respectively.
+* For `aktualizr-device-prov-hsm`
+** put URL to the server backend (together with protocol prefix and port number) at `/var/sota/gateway.url`. If you're using HERE OTA Connect, you can find the URL in the `autoprov.url` file in your credentials archive.
+** put root CA certificate (for the *server*, not for the *device*) at `/var/sota/import/root.crt`.
+** put client certificate and private key to slots 1 and 2 of the PKCS#11-compatible device.
+
+== License
+
+This code is licensed under the link:COPYING.MIT[MIT license], a copy of which can be found in this repository. All code is copyright HERE Europe B.V., 2016-2019.
+
+We require that contributors accept the terms of Linux Foundation's link:https://developercertificate.org/[Developer Certificate of Origin]. Please see the https://github.com/advancedtelematic/aktualizr/blob/master/CONTRIBUTING.md[contribution instructions of aktualizr] for more information.
diff --git a/external/meta-updater/classes/image_repo_manifest.bbclass b/external/meta-updater/classes/image_repo_manifest.bbclass
new file mode 100644
index 00000000..c2e7056d
--- /dev/null
+++ b/external/meta-updater/classes/image_repo_manifest.bbclass
@@ -0,0 +1,23 @@
+# Writes the repo manifest to the target filesystem in /etc/manifest.xml
+#
+# Author: Phil Wise <phil@advancedtelematic.com>
+# Usage: add "inherit image_repo_manifest" to your image file
+# To reproduce a build, copy the /etc/manifest.xml to .repo/manifests/yourname.xml
+# then run:
+# repo init -m yourname.xml
+# repo sync
+# For more information, see:
+# https://web.archive.org/web/20161224194009/https://wiki.cyanogenmod.org/w/Doc:_Using_manifests
+
+HOSTTOOLS_NONFATAL += " repo "
+
+# Write build information to target filesystem
+buildinfo_manifest () {
+ if [ $(which repo) ]; then
+ repo manifest --revision-as-HEAD -o ${IMAGE_ROOTFS}${sysconfdir}/manifest.xml || bbwarn "Android repo tool failed to run; manifest not copied"
+ else
+ bbwarn "Android repo tool not found; manifest not copied."
+ fi
+}
+
+IMAGE_PREPROCESS_COMMAND += "buildinfo_manifest;"
diff --git a/external/meta-updater/classes/image_types_ostree.bbclass b/external/meta-updater/classes/image_types_ostree.bbclass
new file mode 100644
index 00000000..56d4d76c
--- /dev/null
+++ b/external/meta-updater/classes/image_types_ostree.bbclass
@@ -0,0 +1,266 @@
+# OSTree deployment
+inherit distro_features_check
+
+OSTREE_KERNEL ??= "${KERNEL_IMAGETYPE}"
+OSTREE_ROOTFS ??= "${WORKDIR}/ostree-rootfs"
+OSTREE_COMMIT_SUBJECT ??= "Commit-id: ${IMAGE_NAME}"
+OSTREE_COMMIT_BODY ??= ""
+OSTREE_UPDATE_SUMMARY ??= "0"
+OSTREE_DEPLOY_DEVICETREE ??= "0"
+
+BUILD_OSTREE_TARBALL ??= "1"
+
+SYSTEMD_USED = "${@oe.utils.ifelse(d.getVar('VIRTUAL-RUNTIME_init_manager') == 'systemd', 'true', '')}"
+
+IMAGE_CMD_TAR = "tar --xattrs --xattrs-include=*"
+CONVERSION_CMD_tar = "touch ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}; ${IMAGE_CMD_TAR} --numeric-owner -cf ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}.tar -C ${OTA_IMAGE_ROOTFS} . || [ $? -eq 1 ]"
+CONVERSIONTYPES_append = " tar"
+
+REQUIRED_DISTRO_FEATURES = "usrmerge"
+OTA_IMAGE_ROOTFS_task-image-ostree = "${OSTREE_ROOTFS}"
+do_image_ostree[dirs] = "${OSTREE_ROOTFS}"
+do_image_ostree[cleandirs] = "${OSTREE_ROOTFS}"
+do_image_ostree[depends] = "coreutils-native:do_populate_sysroot virtual/kernel:do_deploy ${INITRAMFS_IMAGE}:do_image_complete"
+IMAGE_CMD_ostree () {
+ cp -a ${IMAGE_ROOTFS}/* ${OSTREE_ROOTFS}
+ chmod a+rx ${OSTREE_ROOTFS}
+ sync
+
+ for d in var/*; do
+ if [ "${d}" != "var/local" ]; then
+ rm -rf ${d}
+ fi
+ done
+
+ # Create sysroot directory to which physical sysroot will be mounted
+ mkdir sysroot
+ ln -sf sysroot/ostree ostree
+
+ rm -rf tmp/*
+ ln -sf sysroot/tmp tmp
+
+ mkdir -p usr/rootdirs
+
+ mv etc usr/
+
+ if [ -n "${SYSTEMD_USED}" ]; then
+ mkdir -p usr/etc/tmpfiles.d
+ tmpfiles_conf=usr/etc/tmpfiles.d/00ostree-tmpfiles.conf
+ echo "d /var/rootdirs 0755 root root -" >>${tmpfiles_conf}
+ echo "L /var/rootdirs/home - - - - /sysroot/home" >>${tmpfiles_conf}
+ else
+ mkdir -p usr/etc/init.d
+ tmpfiles_conf=usr/etc/init.d/tmpfiles.sh
+ echo '#!/bin/sh' > ${tmpfiles_conf}
+ echo "mkdir -p /var/rootdirs; chmod 755 /var/rootdirs" >> ${tmpfiles_conf}
+ echo "ln -sf /sysroot/home /var/rootdirs/home" >> ${tmpfiles_conf}
+
+ ln -s ../init.d/tmpfiles.sh usr/etc/rcS.d/S20tmpfiles.sh
+ fi
+
+ # Preserve OSTREE_BRANCHNAME for future information
+ mkdir -p usr/share/sota/
+ echo -n "${OSTREE_BRANCHNAME}" > usr/share/sota/branchname
+
+ # Preserve data in /home to be later copied to /sysroot/home by sysroot
+ # generating procedure
+ mkdir -p usr/homedirs
+ if [ -d "home" ] && [ ! -L "home" ]; then
+ mv home usr/homedirs/home
+ ln -sf var/rootdirs/home home
+ fi
+
+ # Move persistent directories to /var
+ dirs="opt mnt media srv"
+
+ for dir in ${dirs}; do
+ if [ -d ${dir} ] && [ ! -L ${dir} ]; then
+ if [ "$(ls -A $dir)" ]; then
+ bbwarn "Data in /$dir directory is not preserved by OSTree. Consider moving it under /usr"
+ fi
+
+ if [ -n "${SYSTEMD_USED}" ]; then
+ echo "d /var/rootdirs/${dir} 0755 root root -" >>${tmpfiles_conf}
+ else
+ echo "mkdir -p /var/rootdirs/${dir}; chown 755 /var/rootdirs/${dir}" >>${tmpfiles_conf}
+ fi
+ rm -rf ${dir}
+ ln -sf var/rootdirs/${dir} ${dir}
+ fi
+ done
+
+ if [ -d root ] && [ ! -L root ]; then
+ if [ "$(ls -A root)" ]; then
+ bbfatal "Data in /root directory is not preserved by OSTree."
+ fi
+
+ if [ -n "${SYSTEMD_USED}" ]; then
+ echo "d /var/roothome 0755 root root -" >>${tmpfiles_conf}
+ else
+ echo "mkdir -p /var/roothome; chown 755 /var/roothome" >>${tmpfiles_conf}
+ fi
+
+ rm -rf root
+ ln -sf var/roothome root
+ fi
+
+ if [ "${KERNEL_IMAGETYPE}" = "fitImage" ]; then
+ # this is a hack for ostree not to override init= in kernel cmdline -
+ # make it think that the initramfs is present (while it is in FIT image)
+ # since initramfs is fake file, it does not need to be included in checksum
+ checksum=$(sha256sum ${DEPLOY_DIR_IMAGE}/${OSTREE_KERNEL} | cut -f 1 -d " ")
+ touch boot/initramfs-${checksum}
+ else
+ if [ "${OSTREE_DEPLOY_DEVICETREE}" = "1" ] && [ -n "${KERNEL_DEVICETREE}" ]; then
+ checksum=$(cat ${DEPLOY_DIR_IMAGE}/${OSTREE_KERNEL} ${DEPLOY_DIR_IMAGE}/${INITRAMFS_IMAGE}-${MACHINE}.${INITRAMFS_FSTYPES} ${KERNEL_DEVICETREE} | sha256sum | cut -f 1 -d " ")
+ for DTS_FILE in ${KERNEL_DEVICETREE}; do
+ DTS_FILE_BASENAME=$(basename ${DTS_FILE})
+ cp ${DEPLOY_DIR_IMAGE}/${DTS_FILE_BASENAME} boot/devicetree-${DTS_FILE_BASENAME}-${checksum}
+ done
+ else
+ checksum=$(cat ${DEPLOY_DIR_IMAGE}/${OSTREE_KERNEL} ${DEPLOY_DIR_IMAGE}/${INITRAMFS_IMAGE}-${MACHINE}.${INITRAMFS_FSTYPES} | sha256sum | cut -f 1 -d " ")
+ fi
+ cp ${DEPLOY_DIR_IMAGE}/${INITRAMFS_IMAGE}-${MACHINE}.${INITRAMFS_FSTYPES} boot/initramfs-${checksum}
+ fi
+
+ cp ${DEPLOY_DIR_IMAGE}/${OSTREE_KERNEL} boot/vmlinuz-${checksum}
+
+ # Copy image manifest
+ cat ${IMAGE_MANIFEST} | cut -d " " -f1,3 > usr/package.manifest
+}
+
+IMAGE_TYPEDEP_ostreecommit = "ostree"
+do_image_ostreecommit[depends] += "ostree-native:do_populate_sysroot"
+do_image_ostreecommit[lockfiles] += "${OSTREE_REPO}/ostree.lock"
+IMAGE_CMD_ostreecommit () {
+ if ! ostree --repo=${OSTREE_REPO} refs 2>&1 > /dev/null; then
+ ostree --repo=${OSTREE_REPO} init --mode=archive-z2
+ fi
+
+ # Commit the result
+ ostree --repo=${OSTREE_REPO} commit \
+ --tree=dir=${OSTREE_ROOTFS} \
+ --skip-if-unchanged \
+ --branch=${OSTREE_BRANCHNAME} \
+ --subject="${OSTREE_COMMIT_SUBJECT}" \
+ --body="${OSTREE_COMMIT_BODY}"
+
+ if [ "${OSTREE_UPDATE_SUMMARY}" = "1" ]; then
+ ostree --repo=${OSTREE_REPO} summary -u
+ fi
+
+ # To enable simultaneous bitbaking of two images with the same branch name,
+ # create a new ref in the repo using the basename of the image. (This first
+ # requires deleting it if it already exists.) Fixes OTA-2211.
+ ostree --repo=${OSTREE_REPO} refs --delete ${OSTREE_BRANCHNAME}-${IMAGE_BASENAME}
+ ostree_target_hash=$(cat ${OSTREE_REPO}/refs/heads/${OSTREE_BRANCHNAME})
+ ostree --repo=${OSTREE_REPO} refs --create=${OSTREE_BRANCHNAME}-${IMAGE_BASENAME} ${ostree_target_hash}
+}
+
+IMAGE_TYPEDEP_ostreepush = "ostreecommit"
+do_image_ostreepush[depends] += "aktualizr-native:do_populate_sysroot ca-certificates-native:do_populate_sysroot"
+IMAGE_CMD_ostreepush () {
+ # Print warnings if credetials are not set or if the file has not been found.
+ if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
+ if [ -e ${SOTA_PACKED_CREDENTIALS} ]; then
+ garage-push -vv --repo=${OSTREE_REPO} \
+ --ref=${OSTREE_BRANCHNAME} \
+ --credentials=${SOTA_PACKED_CREDENTIALS} \
+ --cacert=${STAGING_ETCDIR_NATIVE}/ssl/certs/ca-certificates.crt
+ else
+ bbwarn "SOTA_PACKED_CREDENTIALS file does not exist."
+ fi
+ else
+ bbwarn "SOTA_PACKED_CREDENTIALS not set. Please add SOTA_PACKED_CREDENTIALS."
+ fi
+}
+
+IMAGE_TYPEDEP_garagesign = "ostreepush"
+do_image_garagesign[depends] += "unzip-native:do_populate_sysroot"
+# This lock solves OTA-1866, which is that removing GARAGE_SIGN_REPO while using
+# garage-sign simultaneously for two images often causes problems.
+do_image_garagesign[lockfiles] += "${DEPLOY_DIR_IMAGE}/garagesign.lock"
+IMAGE_CMD_garagesign () {
+ if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
+ # if credentials are issued by a server that doesn't support offline signing, exit silently
+ unzip -p ${SOTA_PACKED_CREDENTIALS} root.json targets.pub targets.sec tufrepo.url 2>&1 >/dev/null || exit 0
+
+ java_version=$( java -version 2>&1 | awk -F '"' '/version/ {print $2}' )
+ if [ "${java_version}" = "" ]; then
+ bbfatal "Java is required for synchronization with update backend, but is not installed on the host machine"
+ elif [ "${java_version}" \< "1.8" ]; then
+ bbfatal "Java version >= 8 is required for synchronization with update backend"
+ fi
+
+ rm -rf ${GARAGE_SIGN_REPO}
+ garage-sign init --repo tufrepo \
+ --home-dir ${GARAGE_SIGN_REPO} \
+ --credentials ${SOTA_PACKED_CREDENTIALS}
+
+ ostree_target_hash=$(cat ${OSTREE_REPO}/refs/heads/${OSTREE_BRANCHNAME}-${IMAGE_BASENAME})
+
+ # Use OSTree target hash as version if none was provided by the user
+ target_version=${ostree_target_hash}
+ if [ -n "${GARAGE_TARGET_VERSION}" ]; then
+ target_version=${GARAGE_TARGET_VERSION}
+ bbwarn "Target version is overriden with GARAGE_TARGET_VERSION variable. It is a dangerous operation, make sure you've read the respective secion in meta-updater/README.adoc"
+ elif [ -e "${STAGING_DATADIR_NATIVE}/target_version" ]; then
+ target_version=$(cat "${STAGING_DATADIR_NATIVE}/target_version")
+ bbwarn "Target version is overriden with target_version file. It is a dangerous operation, make sure you've read the respective secion in meta-updater/README.adoc"
+ fi
+
+ # Push may fail due to race condition when multiple build machines try to push simultaneously
+ # in which case targets.json should be pulled again and the whole procedure repeated
+ push_success=0
+ target_url=""
+ if [ -n "${GARAGE_TARGET_URL}" ]; then
+ target_url='--url ${GARAGE_TARGET_URL}'
+ fi
+
+ for push_retries in $( seq 3 ); do
+ garage-sign targets pull --repo tufrepo \
+ --home-dir ${GARAGE_SIGN_REPO}
+ garage-sign targets add --repo tufrepo \
+ --home-dir ${GARAGE_SIGN_REPO} \
+ --name ${GARAGE_TARGET_NAME} \
+ --format OSTREE \
+ --version ${target_version} \
+ --length 0 \
+ ${target_url} \
+ --sha256 ${ostree_target_hash} \
+ --hardwareids ${SOTA_HARDWARE_ID}
+ garage-sign targets sign --repo tufrepo \
+ --home-dir ${GARAGE_SIGN_REPO} \
+ --key-name=targets
+ errcode=0
+ garage-sign targets push --repo tufrepo \
+ --home-dir ${GARAGE_SIGN_REPO} || errcode=$?
+ if [ "$errcode" -eq "0" ]; then
+ push_success=1
+ break
+ else
+ bbwarn "Push to garage repository has failed, retrying"
+ fi
+ done
+ rm -rf ${GARAGE_SIGN_REPO}
+
+ if [ "$push_success" -ne "1" ]; then
+ bbfatal "Couldn't push to garage repository"
+ fi
+ fi
+}
+
+IMAGE_TYPEDEP_garagecheck = "garagesign"
+IMAGE_CMD_garagecheck () {
+ if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
+ # if credentials are issued by a server that doesn't support offline signing, exit silently
+ unzip -p ${SOTA_PACKED_CREDENTIALS} root.json targets.pub targets.sec tufrepo.url 2>&1 >/dev/null || exit 0
+
+ ostree_target_hash=$(cat ${OSTREE_REPO}/refs/heads/${OSTREE_BRANCHNAME}-${IMAGE_BASENAME})
+
+ garage-check --ref=${ostree_target_hash} \
+ --credentials=${SOTA_PACKED_CREDENTIALS} \
+ --cacert=${STAGING_ETCDIR_NATIVE}/ssl/certs/ca-certificates.crt
+ fi
+}
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/classes/image_types_ota.bbclass b/external/meta-updater/classes/image_types_ota.bbclass
new file mode 100644
index 00000000..12375ec1
--- /dev/null
+++ b/external/meta-updater/classes/image_types_ota.bbclass
@@ -0,0 +1,113 @@
+# Image to use with u-boot as BIOS and OSTree deployment system
+
+# Boot filesystem size in MiB
+# OSTree updates may require some space on boot file system for
+# boot scripts, kernel and initramfs images
+#
+calculate_size () {
+ BASE=$1
+ SCALE=$2
+ MIN=$3
+ MAX=$4
+ EXTRA=$5
+ ALIGN=$6
+
+ SIZE=`echo "$BASE * $SCALE" | bc -l`
+ REM=`echo $SIZE | cut -d "." -f 2`
+ SIZE=`echo $SIZE | cut -d "." -f 1`
+
+ if [ -n "$REM" -o ! "$REM" -eq 0 ]; then
+ SIZE=`expr $SIZE \+ 1`
+ fi
+
+ if [ "$SIZE" -lt "$MIN" ]; then
+ SIZE=$MIN
+ fi
+
+ SIZE=`expr $SIZE \+ $EXTRA`
+ SIZE=`expr $SIZE \+ $ALIGN \- 1`
+ SIZE=`expr $SIZE \- $SIZE \% $ALIGN`
+
+ if [ -n "$MAX" ]; then
+ if [ "$SIZE" -gt "$MAX" ]; then
+ return -1
+ fi
+ fi
+
+ echo "${SIZE}"
+}
+
+OTA_SYSROOT = "${WORKDIR}/ota-sysroot"
+OTA_IMAGE_ROOTFS_task-image-ota = "${OTA_SYSROOT}"
+IMAGE_TYPEDEP_ota = "ostreecommit"
+do_image_ota[dirs] = "${OTA_SYSROOT}"
+do_image_ota[cleandirs] = "${OTA_SYSROOT}"
+do_image_ota[depends] = "${@'grub:do_populate_sysroot' if d.getVar('OSTREE_BOOTLOADER') == 'grub' else ''} \
+ ${@'virtual/bootloader:do_deploy' if d.getVar('OSTREE_BOOTLOADER') == 'u-boot' else ''}"
+IMAGE_CMD_ota () {
+ ostree admin --sysroot=${OTA_SYSROOT} init-fs ${OTA_SYSROOT}
+ ostree admin --sysroot=${OTA_SYSROOT} os-init ${OSTREE_OSNAME}
+ mkdir -p ${OTA_SYSROOT}/boot/loader.0
+ ln -s loader.0 ${OTA_SYSROOT}/boot/loader
+
+ if [ "${OSTREE_BOOTLOADER}" = "grub" ]; then
+ mkdir -p ${OTA_SYSROOT}/boot/grub2
+ ln -s ../loader/grub.cfg ${OTA_SYSROOT}/boot/grub2/grub.cfg
+ elif [ "${OSTREE_BOOTLOADER}" = "u-boot" ]; then
+ touch ${OTA_SYSROOT}/boot/loader/uEnv.txt
+ else
+ bbfatal "Invalid bootloader: ${OSTREE_BOOTLOADER}"
+ fi
+
+ ostree_target_hash=$(cat ${OSTREE_REPO}/refs/heads/${OSTREE_BRANCHNAME}-${IMAGE_BASENAME})
+
+ ostree --repo=${OTA_SYSROOT}/ostree/repo pull-local --remote=${OSTREE_OSNAME} ${OSTREE_REPO} ${ostree_target_hash}
+ kargs_list=""
+ for arg in ${OSTREE_KERNEL_ARGS}; do
+ kargs_list="${kargs_list} --karg-append=$arg"
+ done
+
+ ostree admin --sysroot=${OTA_SYSROOT} deploy ${kargs_list} --os=${OSTREE_OSNAME} ${ostree_target_hash}
+
+ cp -a ${IMAGE_ROOTFS}/var/sota ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/ || true
+ # Create /var/sota if it doesn't exist yet
+ mkdir -p ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/sota
+ # Ensure the permissions are correctly set
+ chmod 700 ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/sota
+
+ cp -a ${OSTREE_ROOTFS}/var/local ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/ || true
+ cp -a ${OSTREE_ROOTFS}/usr/homedirs/home ${OTA_SYSROOT}/ || true
+ # Ensure that /var/local exists (AGL symlinks /usr/local to /var/local)
+ install -d ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/local
+ # Set package version for the first deployment
+ target_version=${ostree_target_hash}
+ if [ -n "${GARAGE_TARGET_VERSION}" ]; then
+ target_version=${GARAGE_TARGET_VERSION}
+ elif [ -e "${STAGING_DATADIR_NATIVE}/target_version" ]; then
+ target_version=$(cat "${STAGING_DATADIR_NATIVE}/target_version")
+ fi
+ mkdir -p ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/sota/import
+ echo "{\"${ostree_target_hash}\":\"${GARAGE_TARGET_NAME}-${target_version}\"}" > ${OTA_SYSROOT}/ostree/deploy/${OSTREE_OSNAME}/var/sota/import/installed_versions
+}
+
+IMAGE_TYPEDEP_ota-ext4 = "ota"
+do_image_ota_ext4[depends] = "e2fsprogs-native:do_populate_sysroot"
+IMAGE_CMD_ota-ext4 () {
+ # Calculate image size
+ OTA_ROOTFS_SIZE=$(calculate_size `du -ks ${OTA_SYSROOT} | cut -f 1` "${IMAGE_OVERHEAD_FACTOR}" "${IMAGE_ROOTFS_SIZE}" "${IMAGE_ROOTFS_MAXSIZE}" `expr ${IMAGE_ROOTFS_EXTRA_SPACE}` "${IMAGE_ROOTFS_ALIGNMENT}")
+
+ if [ ${OTA_ROOTFS_SIZE} -lt 0 ]; then
+ bbfatal "create_ota failed to calculate OTA rootfs size!"
+ fi
+
+ eval local COUNT=\"0\"
+ eval local MIN_COUNT=\"60\"
+ if [ ${OTA_ROOTFS_SIZE} -lt ${MIN_COUNT} ]; then
+ eval COUNT=\"${MIN_COUNT}\"
+ fi
+
+ dd if=/dev/zero of=${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.ota-ext4 seek=${OTA_ROOTFS_SIZE} count=${COUNT} bs=1024
+ mkfs.ext4 -O ^64bit ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.ota-ext4 -L otaroot -d ${OTA_SYSROOT}
+}
+
+do_image_wic[depends] += "${@bb.utils.contains('DISTRO_FEATURES', 'sota', '%s:do_image_ota_ext4' % d.getVar('IMAGE_BASENAME'), '', d)}"
diff --git a/external/meta-updater/classes/sota.bbclass b/external/meta-updater/classes/sota.bbclass
new file mode 100644
index 00000000..bf27b6dd
--- /dev/null
+++ b/external/meta-updater/classes/sota.bbclass
@@ -0,0 +1,66 @@
+DISTROOVERRIDES .= "${@bb.utils.contains('DISTRO_FEATURES', 'sota', ':sota', '', d)}"
+
+HOSTTOOLS_NONFATAL += "java"
+
+SOTA_CLIENT ??= "aktualizr"
+SOTA_CLIENT_PROV ??= "aktualizr-shared-prov"
+SOTA_DEPLOY_CREDENTIALS ?= "1"
+SOTA_HARDWARE_ID ??= "${MACHINE}"
+
+# Translate old provisioning recipe names into the new versions.
+python () {
+ prov = d.getVar("SOTA_CLIENT_PROV")
+ if prov == "aktualizr-auto-prov":
+ bb.warn('aktualizr-auto-prov is deprecated. Please use aktualizr-shared-prov instead.')
+ d.setVar("SOTA_CLIENT_PROV", "aktualizr-shared-prov")
+ elif prov == "aktualizr-ca-implicit-prov":
+ bb.warn('aktualizr-ca-implicit-prov is deprecated. Please use aktualizr-device-prov instead.')
+ d.setVar("SOTA_CLIENT_PROV", "aktualizr-device-prov")
+ elif prov == "aktualizr-hsm-prov":
+ bb.warn('aktualizr-hsm-prov is deprecated. Please use aktualizr-device-prov-hsm instead.')
+ d.setVar("SOTA_CLIENT_PROV", "aktualizr-device-prov-hsm")
+}
+
+IMAGE_INSTALL_append_sota = " ostree os-release ${SOTA_CLIENT} ${SOTA_CLIENT_PROV}"
+IMAGE_CLASSES += " image_types_ostree image_types_ota"
+
+IMAGE_FSTYPES += "${@bb.utils.contains('DISTRO_FEATURES', 'sota', 'ostreepush garagesign garagecheck ota-ext4 wic', ' ', d)}"
+IMAGE_FSTYPES += "${@bb.utils.contains('BUILD_OSTREE_TARBALL', '1', 'ostree.tar.bz2', ' ', d)}"
+IMAGE_FSTYPES += "${@bb.utils.contains('BUILD_OTA_TARBALL', '1', 'ota.tar.xz', ' ', d)}"
+
+PACKAGECONFIG_append_pn-curl = " ssl"
+PACKAGECONFIG_remove_pn-curl = "gnutls"
+
+WKS_FILE_sota ?= "sdimage-sota.wks"
+
+EXTRA_IMAGEDEPENDS_append_sota = " parted-native mtools-native dosfstools-native"
+
+INITRAMFS_FSTYPES ?= "${@oe.utils.ifelse(d.getVar('OSTREE_BOOTLOADER') == 'u-boot', 'cpio.gz.u-boot', 'cpio.gz')}"
+
+# Please redefine OSTREE_REPO in order to have a persistent OSTree repo
+export OSTREE_REPO ?= "${DEPLOY_DIR_IMAGE}/ostree_repo"
+export OSTREE_BRANCHNAME ?= "${SOTA_HARDWARE_ID}"
+export OSTREE_OSNAME ?= "poky"
+export OSTREE_BOOTLOADER ??= 'u-boot'
+export OSTREE_BOOT_PARTITION ??= "/boot"
+
+INITRAMFS_IMAGE ?= "initramfs-ostree-image"
+
+GARAGE_SIGN_REPO ?= "${DEPLOY_DIR_IMAGE}/garage_sign_repo"
+GARAGE_SIGN_KEYNAME ?= "garage-key"
+GARAGE_TARGET_NAME ?= "${OSTREE_BRANCHNAME}"
+GARAGE_TARGET_VERSION ?= ""
+GARAGE_TARGET_URL ?= ""
+
+SOTA_MACHINE ??="none"
+SOTA_MACHINE_rpi ?= "raspberrypi"
+SOTA_MACHINE_porter ?= "porter"
+SOTA_MACHINE_m3ulcb = "m3ulcb"
+SOTA_MACHINE_intel-corei7-64 ?= "minnowboard"
+SOTA_MACHINE_qemux86-64 ?= "qemux86-64"
+SOTA_MACHINE_am335x-evm ?= "am335x-evm-wifi"
+
+SOTA_OVERRIDES_BLACKLIST = "ostree ota"
+SOTA_REQUIRED_VARIABLES = "OSTREE_REPO OSTREE_BRANCHNAME OSTREE_OSNAME OSTREE_BOOTLOADER OSTREE_BOOT_PARTITION GARAGE_SIGN_REPO GARAGE_TARGET_NAME"
+
+inherit sota_sanity sota_${SOTA_MACHINE} image_repo_manifest
diff --git a/external/meta-updater/classes/sota_am335x-evm-wifi.bbclass b/external/meta-updater/classes/sota_am335x-evm-wifi.bbclass
new file mode 100644
index 00000000..1458d444
--- /dev/null
+++ b/external/meta-updater/classes/sota_am335x-evm-wifi.bbclass
@@ -0,0 +1,15 @@
+KERNEL_IMAGETYPE_sota = "uImage"
+
+OSTREE_BOOTLOADER ?= "u-boot"
+
+EXTRA_IMAGEDEPENDS_append_sota = " acer-bootfiles"
+IMAGE_BOOT_FILES_sota = "bootfiles/*"
+OSTREE_KERNEL_ARGS ?= "ramdisk_size=16384 root=/dev/ram0 rw rootfstype=ext4 rootwait rootdelay=2 ostree_root=/dev/mmcblk0p2 console=ttyO0,115200n8l"
+
+IMAGE_INSTALL_append_sota = " uim iw wl18xx-calibrator wlconf wl18xx-fw hostapd wpa-supplicant"
+
+PREFERRED_VERSION_linux-ti-staging_sota = "4.4.54+gitAUTOINC+ecd4eada6f"
+
+KERNEL_EXTRA_ARGS_append_sota = " LOADADDR=${UBOOT_ENTRYPOINT}"
+
+VIRTUAL-RUNTIME_net_manager_sota = "systemd"
diff --git a/external/meta-updater/classes/sota_bleeding.inc b/external/meta-updater/classes/sota_bleeding.inc
new file mode 100644
index 00000000..fc5947de
--- /dev/null
+++ b/external/meta-updater/classes/sota_bleeding.inc
@@ -0,0 +1 @@
+SRCREV_pn-aktualizr ?= "${AUTOREV}"
diff --git a/external/meta-updater/classes/sota_m3ulcb.bbclass b/external/meta-updater/classes/sota_m3ulcb.bbclass
new file mode 100644
index 00000000..b93cc407
--- /dev/null
+++ b/external/meta-updater/classes/sota_m3ulcb.bbclass
@@ -0,0 +1,11 @@
+# Commit united image to OSTree, not just uImage
+OSTREE_KERNEL = "Image"
+
+EXTRA_IMAGEDEPENDS_append_sota = " m3ulcb-ota-bootfiles"
+IMAGE_BOOT_FILES_sota += "m3ulcb-ota-bootfiles/*"
+
+OSTREE_BOOTLOADER ?= "u-boot"
+UBOOT_MACHINE_sota = "m3ulcb_defconfig"
+
+PREFERRED_RPROVIDER_virtual/network-configuration ?= "connman"
+IMAGE_INSTALL_append_sota = " virtual/network-configuration "
diff --git a/external/meta-updater/classes/sota_minnowboard.bbclass b/external/meta-updater/classes/sota_minnowboard.bbclass
new file mode 100644
index 00000000..a907217d
--- /dev/null
+++ b/external/meta-updater/classes/sota_minnowboard.bbclass
@@ -0,0 +1,12 @@
+OSTREE_BOOTLOADER ?= "grub"
+EFI_PROVIDER_sota = "grub-efi"
+
+WKS_FILE_sota = "efiimage-sota.wks"
+IMAGE_BOOT_FILES_sota = ""
+
+IMAGE_FSTYPES_remove_sota = "live hddimg"
+OSTREE_KERNEL_ARGS ?= "ramdisk_size=16384 rw rootfstype=ext4 rootwait rootdelay=2 console=ttyS0,115200 console=tty0"
+IMAGE_INSTALL_append = " minnowboard-efi-startup"
+
+PREFERRED_RPROVIDER_virtual/network-configuration ?= "connman"
+IMAGE_INSTALL_append_sota = " virtual/network-configuration "
diff --git a/external/meta-updater/classes/sota_none.bbclass b/external/meta-updater/classes/sota_none.bbclass
new file mode 100644
index 00000000..c11b070f
--- /dev/null
+++ b/external/meta-updater/classes/sota_none.bbclass
@@ -0,0 +1,4 @@
+# null machine it's here to make bitbake happy when SOTA_MACHINE is undefined
+#python __anonymous() {
+# bb.warn("SOTA functionality is not yet supported for your machine")
+#}
diff --git a/external/meta-updater/classes/sota_porter.bbclass b/external/meta-updater/classes/sota_porter.bbclass
new file mode 100644
index 00000000..80062e1c
--- /dev/null
+++ b/external/meta-updater/classes/sota_porter.bbclass
@@ -0,0 +1,11 @@
+# Commit united image to OSTree, not just uImage
+OSTREE_KERNEL = "uImage+dtb"
+
+EXTRA_IMAGEDEPENDS_append_sota = " porter-bootfiles"
+IMAGE_BOOT_FILES_sota += "porter-bootfiles/*"
+
+OSTREE_BOOTLOADER ?= "u-boot"
+UBOOT_MACHINE_sota = "porter_config"
+
+PREFERRED_RPROVIDER_virtual/network-configuration ?= "connman"
+IMAGE_INSTALL_append_sota = " virtual/network-configuration "
diff --git a/external/meta-updater/classes/sota_qemux86-64.bbclass b/external/meta-updater/classes/sota_qemux86-64.bbclass
new file mode 100644
index 00000000..fc4aa7cf
--- /dev/null
+++ b/external/meta-updater/classes/sota_qemux86-64.bbclass
@@ -0,0 +1,16 @@
+IMAGE_FSTYPES_remove = "wic"
+
+# U-Boot support for SOTA
+PREFERRED_PROVIDER_virtual/bootloader_sota = "u-boot"
+UBOOT_MACHINE_sota = "qemu-x86_defconfig"
+OSTREE_BOOTLOADER ?= "u-boot"
+INITRAMFS_FSTYPES ?= "cpio.gz"
+
+OSTREE_KERNEL_ARGS ?= "ramdisk_size=16384 rw rootfstype=ext4 rootwait rootdelay=2 ostree_root=/dev/hda"
+
+IMAGE_ROOTFS_EXTRA_SPACE = "${@bb.utils.contains('DISTRO_FEATURES', 'sota', '65536', '', d)}"
+
+# fix for u-boot/swig build issue
+HOSTTOOLS_NONFATAL += "x86_64-linux-gnu-gcc"
+
+IMAGE_INSTALL_append_sota = " virtual/network-configuration "
diff --git a/external/meta-updater/classes/sota_raspberrypi.bbclass b/external/meta-updater/classes/sota_raspberrypi.bbclass
new file mode 100644
index 00000000..69f09fd5
--- /dev/null
+++ b/external/meta-updater/classes/sota_raspberrypi.bbclass
@@ -0,0 +1,42 @@
+RPI_USE_U_BOOT_sota = "1"
+
+KERNEL_CLASSES_append_sota = " kernel-fitimage"
+KERNEL_IMAGETYPE_sota = "fitImage"
+INITRAMFS_FSTYPES = "cpio.gz"
+OSTREE_KERNEL = "${KERNEL_IMAGETYPE}-${INITRAMFS_IMAGE}-${MACHINE}-${KERNEL_FIT_LINK_NAME}"
+
+# DTB needs to be relocated to apply overlays
+UBOOT_DTB_LOADADDRESS = "0x05000000"
+UBOOT_DTBO_LOADADDRESS = "0x06000000"
+
+# Deploy config fragment list to OSTree root fs
+IMAGE_INSTALL_append = " fit-conf"
+
+DEV_MATCH_DIRECTIVE_pn-networkd-dhcp-conf = "Driver=smsc95xx lan78xx"
+IMAGE_INSTALL_append_sota = " virtual/network-configuration "
+
+PREFERRED_PROVIDER_virtual/bootloader_sota ?= "u-boot"
+UBOOT_ENTRYPOINT_sota ?= "0x00008000"
+
+IMAGE_FSTYPES_remove_sota = "rpi-sdimg"
+OSTREE_BOOTLOADER ?= "u-boot"
+
+# OSTree puts its own boot.scr to bcm2835-bootfiles
+IMAGE_BOOT_FILES_sota = "bcm2835-bootfiles/* u-boot.bin;${SDIMG_KERNELIMAGE}"
+
+# Just the overlays that will be used should be listed
+KERNEL_DEVICETREE_raspberrypi2_sota ?= " bcm2709-rpi-2-b.dtb "
+KERNEL_DEVICETREE_raspberrypi3_sota ?= " bcm2710-rpi-3-b.dtb overlays/vc4-kms-v3d.dtbo overlays/rpi-ft5406.dtbo"
+KERNEL_DEVICETREE_raspberrypi3-64_sota ?= " broadcom/bcm2710-rpi-3-b.dtb overlays/vc4-kms-v3d.dtbo overlays/vc4-fkms-v3d.dtbo overlays/rpi-ft5406.dtbo"
+
+SOTA_MAIN_DTB_raspberrypi2 ?= "bcm2709-rpi-2-b.dtb"
+SOTA_MAIN_DTB_raspberrypi3 ?= "bcm2710-rpi-3-b.dtb"
+SOTA_MAIN_DTB_raspberrypi3-64 ?= "broadcom_bcm2710-rpi-3-b.dtb"
+
+SOTA_DT_OVERLAYS_raspberrypi3 ?= "vc4-kms-v3d.dtbo rpi-ft5406.dtbo"
+SOTA_DT_OVERLAYS_raspberrypi3-64 ?= "vc4-kms-v3d.dtbo vc4-fkms-v3d.dtbo rpi-ft5406.dtbo"
+
+# Kernel args normally provided by RPi's internal bootloader. Non-updateable
+OSTREE_KERNEL_ARGS_sota ?= " 8250.nr_uarts=1 bcm2708_fb.fbwidth=656 bcm2708_fb.fbheight=614 bcm2708_fb.fbswap=1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 dwc_otg.lpm_enable=0 console=ttyS0,115200 usbhid.mousepoll=0 "
+
+SOTA_CLIENT_FEATURES_append = " ubootenv"
diff --git a/external/meta-updater/classes/sota_sanity.bbclass b/external/meta-updater/classes/sota_sanity.bbclass
new file mode 100644
index 00000000..8e80acbf
--- /dev/null
+++ b/external/meta-updater/classes/sota_sanity.bbclass
@@ -0,0 +1,54 @@
+# Sanity check the sota setup for common misconfigurations
+
+def sota_check_overrides(status, d):
+ for var in (d.getVar('SOTA_OVERRIDES_BLACKLIST') or "").split():
+ if var in d.getVar('OVERRIDES').split(':'):
+ status.addresult("%s should not be a overrides, because it is a image fstype in updater layer, please check your OVERRIDES setting.\n" % var)
+
+def sota_check_required_variables(status, d):
+ for var in (d.getVar('SOTA_REQUIRED_VARIABLES') or "").split():
+ if not d.getVar(var):
+ status.addresult("%s should be set in your local.conf.\n" % var)
+
+def sota_raise_sanity_error(msg, d):
+ if d.getVar("SANITY_USE_EVENTS") == "1":
+ bb.event.fire(bb.event.SanityCheckFailed(msg), d)
+ return
+
+ bb.fatal("Sota's config sanity checker detected a potential misconfiguration.\n"
+ "Please fix the cause of this error then you can continue to build.\n"
+ "Following is the list of potential problems / advisories:\n"
+ "\n%s" % msg)
+
+def sota_check_sanity(sanity_data):
+ class SanityStatus(object):
+ def __init__(self):
+ self.messages = ""
+ self.reparse = False
+
+ def addresult(self, message):
+ if message:
+ self.messages = self.messages + message
+
+ status = SanityStatus()
+
+ sota_check_overrides(status, sanity_data)
+ sota_check_required_variables(status, sanity_data)
+
+ if status.messages != "":
+ sota_raise_sanity_error(sanity_data.expand(status.messages), sanity_data)
+
+addhandler sota_check_sanity_eventhandler
+sota_check_sanity_eventhandler[eventmask] = "bb.event.SanityCheck"
+
+python sota_check_sanity_eventhandler() {
+ if bb.event.getName(e) == "SanityCheck":
+ sanity_data = copy_data(e)
+ if e.generateevents:
+ sanity_data.setVar("SANITY_USE_EVENTS", "1")
+ reparse = sota_check_sanity(sanity_data)
+ e.data.setVar("BB_INVALIDCONF", reparse)
+ bb.event.fire(bb.event.SanityCheckPassed(), e.data)
+
+ return
+}
diff --git a/external/meta-updater/classes/target_version_example.bbclass b/external/meta-updater/classes/target_version_example.bbclass
new file mode 100644
index 00000000..ef119fb2
--- /dev/null
+++ b/external/meta-updater/classes/target_version_example.bbclass
@@ -0,0 +1,10 @@
+# Writes target version to be used by garage-sign
+
+HOSTTOOLS += " git "
+
+deploy_target_version () {
+ version=$(git --git-dir=${METADIR}/.repo/manifests/.git/ rev-parse HEAD)
+ echo -n ${version} > ${STAGING_DATADIR_NATIVE}/target_version
+}
+
+IMAGE_PREPROCESS_COMMAND += "deploy_target_version;"
diff --git a/external/meta-updater/conf/distro/poky-sota-systemd.conf b/external/meta-updater/conf/distro/poky-sota-systemd.conf
new file mode 100644
index 00000000..b30b322b
--- /dev/null
+++ b/external/meta-updater/conf/distro/poky-sota-systemd.conf
@@ -0,0 +1,12 @@
+require conf/distro/poky.conf
+
+require conf/distro/sota.conf.inc
+
+DISTRO = "poky-sota"
+DISTRO_NAME = "OTA-enabled Linux"
+DISTRO_VERSION = "1.0"
+DISTRO_CODENAME = "sota"
+
+DISTRO_FEATURES_append = " systemd"
+VIRTUAL-RUNTIME_init_manager = "systemd"
+PREFERRED_RPROVIDER_virtual/network-configuration ??= "networkd-dhcp-conf"
diff --git a/external/meta-updater/conf/distro/poky-sota.conf b/external/meta-updater/conf/distro/poky-sota.conf
new file mode 100644
index 00000000..3fb1d204
--- /dev/null
+++ b/external/meta-updater/conf/distro/poky-sota.conf
@@ -0,0 +1,7 @@
+require conf/distro/poky.conf
+require conf/distro/sota.conf.inc
+
+DISTRO = "poky-sota"
+DISTRO_NAME = "OTA-enabled Linux"
+DISTRO_VERSION = "1.0"
+DISTRO_CODENAME = "sota"
diff --git a/external/meta-updater/conf/distro/sota.conf.inc b/external/meta-updater/conf/distro/sota.conf.inc
new file mode 100644
index 00000000..f6111bfc
--- /dev/null
+++ b/external/meta-updater/conf/distro/sota.conf.inc
@@ -0,0 +1,19 @@
+# Common settings to enable SOTA
+# This can be pulled into a custom distribution, or included in
+# local.conf directly with:
+#
+# require conf/distro/sota.conf.inc
+
+DISTRO_FEATURES_append = " sota usrmerge"
+DISTRO_FEATURES_NATIVE_append = " sota"
+INHERIT += " sota"
+# Prelinking increases the size of downloads and causes build errors
+USER_CLASSES_remove = "image-prelink"
+
+# Enable reproducible builds. Use 0 as mtime, the same as OSTree is using.
+INHERIT += "reproducible_build_simple"
+
+export SOURCE_DATE_EPOCH ?= "0"
+REPRODUCIBLE_TIMESTAMP_ROOTFS ?= "0"
+
+HOSTTOOLS_append = " sync sha256sum"
diff --git a/external/meta-updater/conf/include/bblayers/sota.inc b/external/meta-updater/conf/include/bblayers/sota.inc
new file mode 100644
index 00000000..26eea228
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota.inc
@@ -0,0 +1,3 @@
+BBLAYERS += "${METADIR}/meta-updater"
+BBLAYERS += "${METADIR}/meta-openembedded/meta-filesystems"
+BBLAYERS += "${METADIR}/meta-openembedded/meta-oe"
diff --git a/external/meta-updater/conf/include/bblayers/sota_am335x-evm.inc b/external/meta-updater/conf/include/bblayers/sota_am335x-evm.inc
new file mode 100644
index 00000000..909a7a43
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_am335x-evm.inc
@@ -0,0 +1 @@
+BBLAYERS += "${METADIR}/meta-ti"
diff --git a/external/meta-updater/conf/include/bblayers/sota_intel-corei7-64.inc b/external/meta-updater/conf/include/bblayers/sota_intel-corei7-64.inc
new file mode 100644
index 00000000..8e85a6c8
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_intel-corei7-64.inc
@@ -0,0 +1,2 @@
+BBLAYERS += "${METADIR}/meta-updater-minnowboard"
+BBLAYERS += "${METADIR}/meta-intel"
diff --git a/external/meta-updater/conf/include/bblayers/sota_m3ulcb.inc b/external/meta-updater/conf/include/bblayers/sota_m3ulcb.inc
new file mode 100644
index 00000000..4f2e0a91
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_m3ulcb.inc
@@ -0,0 +1,3 @@
+BBLAYERS += "${METADIR}/meta-renesas"
+BBLAYERS += "${METADIR}/meta-renesas-rcar-gen3"
+BBLAYERS += "${METADIR}/meta-openembedded/meta-multimedia"
diff --git a/external/meta-updater/conf/include/bblayers/sota_porter.inc b/external/meta-updater/conf/include/bblayers/sota_porter.inc
new file mode 100644
index 00000000..a50b71cd
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_porter.inc
@@ -0,0 +1,4 @@
+BBLAYERS += "${METADIR}/meta-renesas"
+BBLAYERS += "${METADIR}/meta-renesas/meta-rcar-gen2"
+BBLAYERS += "${METADIR}/meta-openembedded/meta-multimedia"
+BBLAYERS += "${METADIR}/meta-updater-porter"
diff --git a/external/meta-updater/conf/include/bblayers/sota_qemux86-64.inc b/external/meta-updater/conf/include/bblayers/sota_qemux86-64.inc
new file mode 100644
index 00000000..9ccbfa6e
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_qemux86-64.inc
@@ -0,0 +1 @@
+BBLAYERS += "${METADIR}/meta-updater-qemux86-64"
diff --git a/external/meta-updater/conf/include/bblayers/sota_raspberrypi2.inc b/external/meta-updater/conf/include/bblayers/sota_raspberrypi2.inc
new file mode 100644
index 00000000..03f8f44b
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_raspberrypi2.inc
@@ -0,0 +1,3 @@
+BBLAYERS += "${METADIR}/meta-openembedded/meta-python"
+BBLAYERS += "${METADIR}/meta-updater-raspberrypi"
+BBLAYERS += "${METADIR}/meta-raspberrypi"
diff --git a/external/meta-updater/conf/include/bblayers/sota_raspberrypi3-64.inc b/external/meta-updater/conf/include/bblayers/sota_raspberrypi3-64.inc
new file mode 100644
index 00000000..ea420bad
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_raspberrypi3-64.inc
@@ -0,0 +1,2 @@
+BBLAYERS += "${METADIR}/meta-updater-raspberrypi"
+BBLAYERS += "${METADIR}/meta-raspberrypi"
diff --git a/external/meta-updater/conf/include/bblayers/sota_raspberrypi3.inc b/external/meta-updater/conf/include/bblayers/sota_raspberrypi3.inc
new file mode 100644
index 00000000..42d5eb1a
--- /dev/null
+++ b/external/meta-updater/conf/include/bblayers/sota_raspberrypi3.inc
@@ -0,0 +1,4 @@
+BBLAYERS += "${METADIR}/meta-openembedded/meta-python"
+BBLAYERS += "${METADIR}/meta-updater-raspberrypi"
+BBLAYERS += "${METADIR}/meta-raspberrypi"
+BBLAYERS += "${METADIR}/meta-openembedded/meta-networking"
diff --git a/external/meta-updater/conf/layer.conf b/external/meta-updater/conf/layer.conf
new file mode 100644
index 00000000..627a1b8a
--- /dev/null
+++ b/external/meta-updater/conf/layer.conf
@@ -0,0 +1,13 @@
+# We have a conf and classes directory, add to BBPATH
+BBPATH .= ":${LAYERDIR}"
+
+# We have recipes-* directories, add to BBFILES
+BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
+ ${LAYERDIR}/recipes-*/*/*.bbappend"
+
+BBFILE_COLLECTIONS += "sota"
+BBFILE_PATTERN_sota = "^${LAYERDIR}/"
+BBFILE_PRIORITY_sota = "7"
+
+LAYERDEPENDS_sota = "filesystems-layer"
+LAYERSERIES_COMPAT_sota = "thud"
diff --git a/external/meta-updater/conf/local.conf.sample.append b/external/meta-updater/conf/local.conf.sample.append
new file mode 100644
index 00000000..4588ec3a
--- /dev/null
+++ b/external/meta-updater/conf/local.conf.sample.append
@@ -0,0 +1,35 @@
+
+#
+# meta-updater configuration, see README.adoc and aktualizr's
+# documentation for more options and detailed documentation
+#
+
+MACHINE = "##MACHINE##"
+DISTRO = "poky-sota-systemd"
+
+# General SOTA setup
+#SOTA_CLIENT_PROV = "aktualizr-auto-prov"
+#SOTA_PACKED_CREDENTIALS = "/path/to/credentials.zip"
+
+# Uncomment this line to start an ssh server at boot automatically
+#IMAGE_FEATURES += "ssh-server-dropbear"
+
+# Uncomment this line to set the log level of aktualizr to 'debug' (from 'info'
+# by default)
+#IMAGE_INSTALL_append += " aktualizr-log-debug"
+
+# Store systemd logs in persistent storage
+#
+# It greatly helps diagnosing issues on testing devices but should be
+# carefully weighted against file system usage and flash device wear for
+# production systems. Please refer to systemd's docs for more details
+IMAGE_INSTALL_append += " systemd-journald-persistent"
+
+# Set resource limits for aktualizr service
+#
+# Uncomment these lines to change the default parameters.
+#
+#RESOURCE_CPU_WEIGHT_pn-aktualizr = "100"
+#RESOURCE_MEMORY_HIGH_pn-aktualizr = "100M"
+#RESOURCE_MEMORY_MAX_pn-aktualizr = "80%"
+IMAGE_INSTALL_append += " aktualizr-resource-control"
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/qemucommand.py b/external/meta-updater/lib/oeqa/selftest/cases/qemucommand.py
new file mode 120000
index 00000000..075cdb8f
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/qemucommand.py
@@ -0,0 +1 @@
+../../../../scripts/qemucommand.py \ No newline at end of file
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/testutils.py b/external/meta-updater/lib/oeqa/selftest/cases/testutils.py
new file mode 100644
index 00000000..8d618a68
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/testutils.py
@@ -0,0 +1,143 @@
+import os
+import oe.path
+import logging
+import re
+import subprocess
+from time import sleep
+
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+from qemucommand import QemuCommand
+
+logger = logging.getLogger("selftest")
+
+
+def qemu_launch(efi=False, machine=None, imagename='core-image-minimal', **kwargs):
+ qemu_bake_image(imagename)
+ return qemu_boot_image(efi=efi, machine=machine, imagename=imagename, **kwargs)
+
+
+def qemu_terminate(s):
+ try:
+ s.terminate()
+ s.wait(timeout=10)
+ except KeyboardInterrupt:
+ pass
+
+
+def qemu_boot_image(imagename, **kwargs):
+ # Create empty object.
+ args = type('', (), {})()
+ args.imagename = imagename
+ args.mac = kwargs.get('mac', None)
+ # Could use DEPLOY_DIR_IMAGE here but it's already in the machine
+ # subdirectory.
+ args.dir = 'tmp/deploy/images'
+ args.efi = kwargs.get('efi', False)
+ args.machine = kwargs.get('machine', None)
+ args.mem = kwargs.get('mem', '128M')
+ qemu_use_kvm = get_bb_var("QEMU_USE_KVM")
+ if qemu_use_kvm and \
+ (qemu_use_kvm == 'True' and 'x86' in args.machine or
+ get_bb_var('MACHINE') in qemu_use_kvm.split()):
+ args.kvm = True
+ else:
+ args.kvm = None # Autodetect
+ args.no_gui = kwargs.get('no_gui', True)
+ args.gdb = kwargs.get('gdb', False)
+ args.pcap = kwargs.get('pcap', None)
+ args.overlay = kwargs.get('overlay', None)
+ args.dry_run = kwargs.get('dry_run', False)
+ args.secondary_network = kwargs.get('secondary_network', False)
+
+ qemu = QemuCommand(args)
+ cmdline = qemu.command_line()
+ print('Booting image with run-qemu-ota...')
+ s = subprocess.Popen(cmdline)
+ sleep(kwargs.get('wait_for_boot_time', 10))
+ return qemu, s
+
+
+def qemu_bake_image(imagename):
+ logger.info('Running bitbake to build {}'.format(imagename))
+ bitbake(imagename)
+
+
+def qemu_send_command(port, command, timeout=120):
+ command = ['ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@localhost -p ' +
+ str(port) + ' "' + command + '"']
+ s2 = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = s2.communicate(timeout=timeout)
+ return stdout, stderr, s2.returncode
+
+
+def metadir():
+ # Assume the directory layout for finding other layers. We could also
+ # make assumptions by using 'show-layers', but either way, if the
+ # layers we need aren't where we expect them, we are out of luck.
+ path = os.path.abspath(os.path.dirname(__file__))
+ metadir = path + "/../../../../../"
+
+ return metadir
+
+
+def akt_native_run(testInst, cmd, **kwargs):
+ # run a command supplied by aktualizr-native and checks that:
+ # - the executable exists
+ # - the command runs without error
+ #
+ # Requirements in base test class (setUpClass for example):
+ # bitbake aktualizr-native
+ # bitbake build-sysroots -c build_native_sysroot
+ #
+ # (technique found in poky/meta/lib/oeqa/selftest/cases/package.py)
+ bb_vars = get_bb_vars(['STAGING_DIR', 'BUILD_ARCH'])
+ sysroot = oe.path.join(bb_vars['STAGING_DIR'], bb_vars['BUILD_ARCH'])
+
+ result = runCmd(cmd, native_sysroot=sysroot, ignore_status=True, **kwargs)
+ testInst.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
+
+
+def verifyNotProvisioned(testInst, machine):
+ print('Checking output of aktualizr-info:')
+ ran_ok = False
+ for delay in [5, 5, 5, 5, 10, 10, 10, 10]:
+ stdout, stderr, retcode = testInst.qemu_command('aktualizr-info')
+ if retcode == 0 and stderr == b'':
+ ran_ok = True
+ break
+ sleep(delay)
+ testInst.assertTrue(ran_ok, 'aktualizr-info failed: ' + stderr.decode() + stdout.decode())
+
+ # Verify that device has NOT yet provisioned.
+ testInst.assertIn(b'Couldn\'t load device ID', stdout,
+ 'Device already provisioned!? ' + stderr.decode() + stdout.decode())
+ testInst.assertIn(b'Couldn\'t load ECU serials', stdout,
+ 'Device already provisioned!? ' + stderr.decode() + stdout.decode())
+ testInst.assertIn(b'Provisioned on server: no', stdout,
+ 'Device already provisioned!? ' + stderr.decode() + stdout.decode())
+ testInst.assertIn(b'Fetched metadata: no', stdout,
+ 'Device already provisioned!? ' + stderr.decode() + stdout.decode())
+
+
+def verifyProvisioned(testInst, machine):
+ # Verify that device HAS provisioned.
+ ran_ok = False
+ for delay in [5, 5, 5, 5, 10, 10, 10, 10]:
+ stdout, stderr, retcode = testInst.qemu_command('aktualizr-info')
+ if retcode == 0 and stderr == b'' and stdout.decode().find('Fetched metadata: yes') >= 0:
+ ran_ok = True
+ break
+ sleep(delay)
+ testInst.assertTrue(ran_ok, 'aktualizr-info failed: ' + stderr.decode() + stdout.decode())
+
+ testInst.assertIn(b'Device ID: ', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode())
+ testInst.assertIn(b'Primary ecu hardware ID: ' + machine.encode(), stdout,
+ 'Provisioning failed: ' + stderr.decode() + stdout.decode())
+ testInst.assertIn(b'Fetched metadata: yes', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode())
+ p = re.compile(r'Device ID: ([a-z0-9-]*)\n')
+ m = p.search(stdout.decode())
+ testInst.assertTrue(m, 'Device ID could not be read: ' + stderr.decode() + stdout.decode())
+ testInst.assertGreater(m.lastindex, 0, 'Device ID could not be read: ' + stderr.decode() + stdout.decode())
+ logger.info('Device successfully provisioned with ID: ' + m.group(1))
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/updater_minnowboard.py b/external/meta-updater/lib/oeqa/selftest/cases/updater_minnowboard.py
new file mode 100644
index 00000000..f4da3605
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/updater_minnowboard.py
@@ -0,0 +1,54 @@
+import re
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, get_bb_var
+from testutils import metadir, qemu_launch, qemu_send_command, qemu_terminate, verifyProvisioned
+
+
+class MinnowTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ layer_intel = "meta-intel"
+ layer_minnow = "meta-updater-minnowboard"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer_intel, result.output) is None:
+ self.meta_intel = metadir() + layer_intel
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_intel)
+ else:
+ self.meta_intel = None
+ if re.search(layer_minnow, result.output) is None:
+ self.meta_minnow = metadir() + layer_minnow
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_minnow)
+ else:
+ self.meta_minnow = None
+ self.append_config('MACHINE = "intel-corei7-64"')
+ self.append_config('OSTREE_BOOTLOADER = "grub"')
+ self.append_config('SOTA_CLIENT_PROV = " aktualizr-shared-prov "')
+ self.qemu, self.s = qemu_launch(efi=True, machine='intel-corei7-64', mem='512M')
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_intel:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_intel, ignore_status=True)
+ if self.meta_minnow:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_minnow, ignore_status=True)
+
+ def qemu_command(self, command):
+ return qemu_send_command(self.qemu.ssh_port, command)
+
+ def test_provisioning(self):
+ print('Checking machine name (hostname) of device:')
+ stdout, stderr, retcode = self.qemu_command('hostname')
+ self.assertEqual(retcode, 0, "Unable to check hostname. " +
+ "Is an ssh daemon (such as dropbear or openssh) installed on the device?")
+ machine = get_bb_var('MACHINE', 'core-image-minimal')
+ self.assertEqual(stderr, b'', 'Error: ' + stderr.decode())
+ # Strip off line ending.
+ value = stdout.decode()[:-1]
+ self.assertEqual(value, machine,
+ 'MACHINE does not match hostname: ' + machine + ', ' + value +
+ '\nIs TianoCore ovmf installed on your host machine?')
+
+ verifyProvisioned(self, machine)
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/updater_native.py b/external/meta-updater/lib/oeqa/selftest/cases/updater_native.py
new file mode 100644
index 00000000..d2bf341d
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/updater_native.py
@@ -0,0 +1,48 @@
+# pylint: disable=C0111,C0325
+import logging
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from testutils import akt_native_run
+
+
+class SotaToolsTests(OESelftestTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(SotaToolsTests, cls).setUpClass()
+ logger = logging.getLogger("selftest")
+ logger.info('Running bitbake to build aktualizr-native tools')
+ bitbake('aktualizr-native')
+ bitbake('build-sysroots -c build_native_sysroot')
+
+ def test_push_help(self):
+ akt_native_run(self, 'garage-push --help')
+
+ def test_deploy_help(self):
+ akt_native_run(self, 'garage-deploy --help')
+
+ def test_garagesign_help(self):
+ akt_native_run(self, 'garage-sign --help')
+
+
+class GeneralTests(OESelftestTestCase):
+
+ def test_feature_sota(self):
+ result = get_bb_var('DISTRO_FEATURES').find('sota')
+ self.assertNotEqual(result, -1, 'Feature "sota" not set at DISTRO_FEATURES')
+
+ def test_feature_usrmerge(self):
+ result = get_bb_var('DISTRO_FEATURES').find('usrmerge')
+ self.assertNotEqual(result, -1, 'Feature "sota" not set at DISTRO_FEATURES')
+
+ def test_feature_systemd(self):
+ result = get_bb_var('DISTRO_FEATURES').find('systemd')
+ self.assertNotEqual(result, -1, 'Feature "systemd" not set at DISTRO_FEATURES')
+
+ def test_java(self):
+ result = runCmd('which java', ignore_status=True)
+ self.assertEqual(result.status, 0,
+ "Java not found. Do you have a JDK installed on your host machine?")
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64.py b/external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64.py
new file mode 100644
index 00000000..2b4726cb
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64.py
@@ -0,0 +1,476 @@
+# pylint: disable=C0111,C0325
+import os
+import logging
+import re
+import subprocess
+import unittest
+from time import sleep
+from uuid import uuid4
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+from testutils import qemu_launch, qemu_send_command, qemu_terminate, \
+ metadir, akt_native_run, verifyNotProvisioned, verifyProvisioned, \
+ qemu_bake_image, qemu_boot_image
+
+
+class GeneralTests(OESelftestTestCase):
+ def test_credentials(self):
+ logger = logging.getLogger("selftest")
+ logger.info('Running bitbake to build core-image-minimal')
+ self.append_config('SOTA_CLIENT_PROV = "aktualizr-shared-prov"')
+
+ # note: this also tests ostreepush/garagesign/garagecheck which are
+ # omitted from other test cases
+ bitbake('core-image-minimal')
+ credentials = get_bb_var('SOTA_PACKED_CREDENTIALS')
+ # skip the test if the variable SOTA_PACKED_CREDENTIALS is not set
+ if credentials is None:
+ raise unittest.SkipTest("Variable 'SOTA_PACKED_CREDENTIALS' not set.")
+ # Check if the file exists
+ self.assertTrue(os.path.isfile(credentials), "File %s does not exist" % credentials)
+ deploydir = get_bb_var('DEPLOY_DIR_IMAGE')
+ imagename = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
+ # Check if the credentials are included in the output image
+ result = runCmd('tar -jtvf %s/%s.tar.bz2 | grep sota_provisioning_credentials.zip' %
+ (deploydir, imagename), ignore_status=True)
+ self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
+
+
+class AktualizrToolsTests(OESelftestTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(AktualizrToolsTests, cls).setUpClass()
+ logger = logging.getLogger("selftest")
+ logger.info('Running bitbake to build aktualizr-native tools')
+ bitbake('aktualizr-native aktualizr-device-prov')
+ bitbake('build-sysroots -c build_native_sysroot')
+
+ def test_cert_provider_help(self):
+ akt_native_run(self, 'aktualizr-cert-provider --help')
+
+ def test_cert_provider_local_output(self):
+ bb_vars = get_bb_vars(['SOTA_PACKED_CREDENTIALS', 'T'], 'aktualizr-native')
+ creds = bb_vars['SOTA_PACKED_CREDENTIALS']
+ temp_dir = bb_vars['T']
+ bb_vars_prov = get_bb_vars(['WORKDIR', 'libdir'], 'aktualizr-device-prov')
+ config = bb_vars_prov['WORKDIR'] + '/sysroot-destdir' + bb_vars_prov['libdir'] + '/sota/conf.d/20-sota-device-cred.toml'
+
+ akt_native_run(self, 'aktualizr-cert-provider -c {creds} -r -l {temp} -g {config}'
+ .format(creds=creds, temp=temp_dir, config=config))
+
+ # Might be nice if these names weren't hardcoded.
+ cert_path = temp_dir + '/var/sota/import/client.pem'
+ self.assertTrue(os.path.isfile(cert_path), "Client certificate not found at %s." % cert_path)
+ self.assertTrue(os.path.getsize(cert_path) > 0, "Client certificate at %s is empty." % cert_path)
+ pkey_path = temp_dir + '/var/sota/import/pkey.pem'
+ self.assertTrue(os.path.isfile(pkey_path), "Private key not found at %s." % pkey_path)
+ self.assertTrue(os.path.getsize(pkey_path) > 0, "Private key at %s is empty." % pkey_path)
+ ca_path = temp_dir + '/var/sota/import/root.crt'
+ self.assertTrue(os.path.isfile(ca_path), "Client certificate not found at %s." % ca_path)
+ self.assertTrue(os.path.getsize(ca_path) > 0, "Client certificate at %s is empty." % ca_path)
+
+
+class SharedCredProvTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+ self.append_config('MACHINE = "qemux86-64"')
+ self.append_config('SOTA_CLIENT_PROV = " aktualizr-shared-prov "')
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.qemu, self.s = qemu_launch(machine='qemux86-64')
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def qemu_command(self, command):
+ return qemu_send_command(self.qemu.ssh_port, command)
+
+ def test_provisioning(self):
+ print('Checking machine name (hostname) of device:')
+ stdout, stderr, retcode = self.qemu_command('hostname')
+ self.assertEqual(retcode, 0, "Unable to check hostname. " +
+ "Is an ssh daemon (such as dropbear or openssh) installed on the device?")
+ machine = get_bb_var('MACHINE', 'core-image-minimal')
+ self.assertEqual(stderr, b'', 'Error: ' + stderr.decode())
+ # Strip off line ending.
+ value = stdout.decode()[:-1]
+ self.assertEqual(value, machine,
+ 'MACHINE does not match hostname: ' + machine + ', ' + value)
+
+ verifyProvisioned(self, machine)
+
+
+class ManualControlTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+ self.append_config('MACHINE = "qemux86-64"')
+ self.append_config('SOTA_CLIENT_PROV = " aktualizr-shared-prov "')
+ self.append_config('SYSTEMD_AUTO_ENABLE_aktualizr = "disable"')
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.qemu, self.s = qemu_launch(machine='qemux86-64')
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def qemu_command(self, command):
+ return qemu_send_command(self.qemu.ssh_port, command)
+
+ def test_manual_run_mode_once(self):
+ """
+ Disable the systemd service then run aktualizr manually
+ """
+ sleep(20)
+ stdout, stderr, retcode = self.qemu_command('aktualizr-info')
+ self.assertIn(b'Can\'t open database', stderr,
+ 'Aktualizr should not have run yet' + stderr.decode() + stdout.decode())
+
+ stdout, stderr, retcode = self.qemu_command('aktualizr once')
+
+ stdout, stderr, retcode = self.qemu_command('aktualizr-info')
+ self.assertIn(b'Fetched metadata: yes', stdout,
+ 'Aktualizr should have run' + stderr.decode() + stdout.decode())
+
+
+class DeviceCredProvTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+ self.append_config('MACHINE = "qemux86-64"')
+ self.append_config('SOTA_CLIENT_PROV = " aktualizr-device-prov "')
+ self.append_config('SOTA_DEPLOY_CREDENTIALS = "0"')
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.qemu, self.s = qemu_launch(machine='qemux86-64')
+ bitbake('build-sysroots -c build_native_sysroot')
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def qemu_command(self, command):
+ return qemu_send_command(self.qemu.ssh_port, command)
+
+ def test_provisioning(self):
+ print('Checking machine name (hostname) of device:')
+ stdout, stderr, retcode = self.qemu_command('hostname')
+ self.assertEqual(retcode, 0, "Unable to check hostname. " +
+ "Is an ssh daemon (such as dropbear or openssh) installed on the device?")
+ machine = get_bb_var('MACHINE', 'core-image-minimal')
+ self.assertEqual(stderr, b'', 'Error: ' + stderr.decode())
+ # Strip off line ending.
+ value = stdout.decode()[:-1]
+ self.assertEqual(value, machine,
+ 'MACHINE does not match hostname: ' + machine + ', ' + value)
+
+ verifyNotProvisioned(self, machine)
+
+ # Run aktualizr-cert-provider.
+ bb_vars = get_bb_vars(['SOTA_PACKED_CREDENTIALS'], 'aktualizr-native')
+ creds = bb_vars['SOTA_PACKED_CREDENTIALS']
+ bb_vars_prov = get_bb_vars(['WORKDIR', 'libdir'], 'aktualizr-device-prov')
+ config = bb_vars_prov['WORKDIR'] + '/sysroot-destdir' + bb_vars_prov['libdir'] + '/sota/conf.d/20-sota-device-cred.toml'
+
+ print('Provisining at root@localhost:%d' % self.qemu.ssh_port)
+ akt_native_run(self, 'aktualizr-cert-provider -c {creds} -t root@localhost -p {port} -s -u -r -g {config}'
+ .format(creds=creds, port=self.qemu.ssh_port, config=config))
+
+ verifyProvisioned(self, machine)
+
+
+class DeviceCredProvHsmTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+ self.append_config('MACHINE = "qemux86-64"')
+ self.append_config('SOTA_CLIENT_PROV = "aktualizr-device-prov-hsm"')
+ self.append_config('SOTA_DEPLOY_CREDENTIALS = "0"')
+ self.append_config('SOTA_CLIENT_FEATURES = "hsm"')
+ self.append_config('IMAGE_INSTALL_append = " softhsm-testtoken"')
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.qemu, self.s = qemu_launch(machine='qemux86-64')
+ bitbake('build-sysroots -c build_native_sysroot')
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def qemu_command(self, command):
+ return qemu_send_command(self.qemu.ssh_port, command)
+
+ def test_provisioning(self):
+ print('Checking machine name (hostname) of device:')
+ stdout, stderr, retcode = self.qemu_command('hostname')
+ self.assertEqual(retcode, 0, "Unable to check hostname. " +
+ "Is an ssh daemon (such as dropbear or openssh) installed on the device?")
+ machine = get_bb_var('MACHINE', 'core-image-minimal')
+ self.assertEqual(stderr, b'', 'Error: ' + stderr.decode())
+ # Strip off line ending.
+ value = stdout.decode()[:-1]
+ self.assertEqual(value, machine,
+ 'MACHINE does not match hostname: ' + machine + ', ' + value)
+
+ verifyNotProvisioned(self, machine)
+
+ # Verify that HSM is not yet initialized.
+ pkcs11_command = 'pkcs11-tool --module=/usr/lib/softhsm/libsofthsm2.so -O'
+ stdout, stderr, retcode = self.qemu_command(pkcs11_command)
+ self.assertNotEqual(retcode, 0, 'pkcs11-tool succeeded before initialization: ' +
+ stdout.decode() + stderr.decode())
+ softhsm2_command = 'softhsm2-util --show-slots'
+ stdout, stderr, retcode = self.qemu_command(softhsm2_command)
+ self.assertNotEqual(retcode, 0, 'softhsm2-tool succeeded before initialization: ' +
+ stdout.decode() + stderr.decode())
+
+ # Run aktualizr-cert-provider.
+ bb_vars = get_bb_vars(['SOTA_PACKED_CREDENTIALS'], 'aktualizr-native')
+ creds = bb_vars['SOTA_PACKED_CREDENTIALS']
+ bb_vars_prov = get_bb_vars(['WORKDIR', 'libdir'], 'aktualizr-device-prov-hsm')
+ config = bb_vars_prov['WORKDIR'] + '/sysroot-destdir' + bb_vars_prov['libdir'] + '/sota/conf.d/20-sota-device-cred-hsm.toml'
+
+ akt_native_run(self, 'aktualizr-cert-provider -c {creds} -t root@localhost -p {port} -r -s -u -g {config}'
+ .format(creds=creds, port=self.qemu.ssh_port, config=config))
+
+ # Verify that HSM is able to initialize.
+ for delay in [5, 5, 5, 5, 10]:
+ sleep(delay)
+ p11_out, p11_err, p11_ret = self.qemu_command(pkcs11_command)
+ hsm_out, hsm_err, hsm_ret = self.qemu_command(softhsm2_command)
+ if (p11_ret == 0 and hsm_ret == 0 and hsm_err == b'' and
+ b'X.509 cert' in p11_out and b'present token' in p11_err):
+ break
+ else:
+ self.fail('pkcs11-tool or softhsm2-tool failed: ' + p11_err.decode() +
+ p11_out.decode() + hsm_err.decode() + hsm_out.decode())
+
+ self.assertIn(b'Initialized: yes', hsm_out, 'softhsm2-tool failed: ' +
+ hsm_err.decode() + hsm_out.decode())
+ self.assertIn(b'User PIN init.: yes', hsm_out, 'softhsm2-tool failed: ' +
+ hsm_err.decode() + hsm_out.decode())
+
+ # Check that pkcs11 output matches sofhsm output.
+ p11_p = re.compile(r'Using slot [0-9] with a present token \((0x[0-9a-f]*)\)\s')
+ p11_m = p11_p.search(p11_err.decode())
+ self.assertTrue(p11_m, 'Slot number not found with pkcs11-tool: ' + p11_err.decode() + p11_out.decode())
+ self.assertGreater(p11_m.lastindex, 0, 'Slot number not found with pkcs11-tool: ' +
+ p11_err.decode() + p11_out.decode())
+ hsm_p = re.compile(r'Description:\s*SoftHSM slot ID (0x[0-9a-f]*)\s')
+ hsm_m = hsm_p.search(hsm_out.decode())
+ self.assertTrue(hsm_m, 'Slot number not found with softhsm2-tool: ' + hsm_err.decode() + hsm_out.decode())
+ self.assertGreater(hsm_m.lastindex, 0, 'Slot number not found with softhsm2-tool: ' +
+ hsm_err.decode() + hsm_out.decode())
+ self.assertEqual(p11_m.group(1), hsm_m.group(1), 'Slot number does not match: ' +
+ p11_err.decode() + p11_out.decode() + hsm_err.decode() + hsm_out.decode())
+
+ verifyProvisioned(self, machine)
+
+
+class IpSecondaryTests(OESelftestTestCase):
+
+ class Image:
+ def __init__(self, imagename, binaryname, machine='qemux86-64', bake=True, **kwargs):
+ self.machine = machine
+ self.imagename = imagename
+ self.boot_kwargs = kwargs
+ self.binaryname = binaryname
+ self.stdout = ''
+ self.stderr = ''
+ self.retcode = 0
+ if bake:
+ self.bake()
+
+ def bake(self):
+ self.configure()
+ qemu_bake_image(self.imagename)
+
+ def send_command(self, cmd, timeout=60):
+ stdout, stderr, retcode = qemu_send_command(self.qemu.ssh_port, cmd, timeout=timeout)
+ return str(stdout), str(stderr), retcode
+
+ def __enter__(self):
+ self.qemu, self.process = qemu_boot_image(machine=self.machine, imagename=self.imagename,
+ wait_for_boot_time=1, **self.boot_kwargs)
+ # wait until the VM is booted and is SSHable
+ self.wait_till_sshable()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ qemu_terminate(self.process)
+
+ def wait_till_sshable(self):
+ # qemu_send_command tries to ssh into the qemu VM and blocks until it gets there or timeout happens
+ # so it helps us to block q control flow until the VM is booted and a target binary/daemon is running there
+ self.stdout, self.stderr, self.retcode = self.send_command(self.binaryname + ' --help', timeout=300)
+
+ def was_successfully_booted(self):
+ return self.retcode == 0
+
+ class Secondary(Image):
+ def __init__(self, test_ctx):
+ self._test_ctx = test_ctx
+ self.sndry_serial = str(uuid4())
+ self.sndry_hw_id = 'qemux86-64-oeselftest-sndry'
+ self.id = (self.sndry_hw_id, self.sndry_serial)
+ super(IpSecondaryTests.Secondary, self).__init__('secondary-image', 'aktualizr-secondary',
+ secondary_network=True)
+
+ def configure(self):
+ self._test_ctx.append_config('SECONDARY_SERIAL_ID = "{}"'.format(self.sndry_serial))
+ self._test_ctx.append_config('SECONDARY_HARDWARE_ID = "{}"'.format(self.sndry_hw_id))
+
+ class Primary(Image):
+ def __init__(self, test_ctx):
+ self._test_ctx = test_ctx
+ super(IpSecondaryTests.Primary, self).__init__('primary-image', 'aktualizr', secondary_network=True)
+
+ def configure(self):
+ self._test_ctx.append_config('MACHINE = "qemux86-64"')
+ self._test_ctx.append_config('SOTA_CLIENT_PROV = " aktualizr-shared-prov "')
+
+ def is_ecu_registered(self, ecu_id):
+ max_number_of_tries = 40
+ try_counter = 0
+
+ # aktualizr-info is not always able to load ECU serials from DB
+ # so, let's run it a few times until it actually succeeds
+ while try_counter < max_number_of_tries:
+ device_status = self.get_info()
+ try_counter += 1
+ if device_status.find("load ECU serials") == -1:
+ break
+ sleep(1)
+
+ if not ((device_status.find(ecu_id[0]) != -1) and (device_status.find(ecu_id[1]) != -1)):
+ return False
+ not_registered_field = "Removed or not registered ecus:"
+ not_reg_start = device_status.find(not_registered_field)
+ return not_reg_start == -1 or (device_status.find(ecu_id[1], not_reg_start) == -1)
+
+ def get_info(self):
+ stdout, stderr, retcode = self.send_command('aktualizr-info')
+ self._test_ctx.assertEqual(retcode, 0, 'Unable to run aktualizr-info: {}'.format(stderr))
+ return stdout
+
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.primary = IpSecondaryTests.Primary(self)
+ self.secondary = IpSecondaryTests.Secondary(self)
+
+ def tearDownLocal(self):
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def test_ip_secondary_registration_if_secondary_starts_first(self):
+ with self.secondary:
+ self.assertTrue(self.secondary.was_successfully_booted(),
+ 'The secondary failed to boot: {}'.format(self.secondary.stderr))
+
+ with self.primary:
+ self.assertTrue(self.primary.was_successfully_booted(),
+ 'The primary failed to boot: {}'.format(self.primary.stderr))
+
+ self.assertTrue(self.primary.is_ecu_registered(self.secondary.id),
+ "The secondary wasn't registered at the primary: {}".format(self.primary.get_info()))
+
+ def test_ip_secondary_registration_if_primary_starts_first(self):
+ with self.primary:
+ self.assertTrue(self.primary.was_successfully_booted(),
+ 'The primary failed to boot: {}'.format(self.primary.stderr))
+
+ with self.secondary:
+ self.assertTrue(self.secondary.was_successfully_booted(),
+ 'The secondary failed to boot: {}'.format(self.secondary.stderr))
+
+ self.assertTrue(self.primary.is_ecu_registered(self.secondary.id),
+ "The secondary wasn't registered at the primary: {}".format(self.primary.get_info()))
+
+
+class ResourceControlTests(OESelftestTestCase):
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+ self.append_config('MACHINE = "qemux86-64"')
+ self.append_config('SOTA_CLIENT_PROV = " aktualizr-shared-prov "')
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.append_config('IMAGE_INSTALL_append += " aktualizr-resource-control "')
+ self.append_config('RESOURCE_CPU_WEIGHT_pn-aktualizr = "1000"')
+ self.append_config('RESOURCE_MEMORY_HIGH_pn-aktualizr = "50M"')
+ self.append_config('RESOURCE_MEMORY_MAX_pn-aktualizr = "1M"')
+ self.qemu, self.s = qemu_launch(machine='qemux86-64')
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def qemu_command(self, command):
+ return qemu_send_command(self.qemu.ssh_port, command)
+
+ def test_aktualizr_resource_control(self):
+ print('Checking aktualizr was killed')
+ ran_ok = False
+ for delay in [5, 5, 5, 5]:
+ sleep(delay)
+ try:
+ stdout, stderr, retcode = self.qemu_command('systemctl --no-pager show aktualizr')
+ if retcode == 0 and b'ExecMainStatus=9' in stdout:
+ ran_ok = True
+ break
+ except subprocess.TimeoutExpired:
+ pass
+ self.assertTrue(ran_ok, 'Aktualizr was not killed')
+
+ self.assertIn(b'CPUWeight=1000', stdout, 'CPUWeight was not set correctly')
+ self.assertIn(b'MemoryHigh=52428800', stdout, 'MemoryHigh was not set correctly')
+ self.assertIn(b'MemoryMax=1048576', stdout, 'MemoryMax was not set correctly')
+
+ self.qemu_command('systemctl --runtime set-property aktualizr MemoryMax=')
+ self.qemu_command('systemctl restart aktualizr')
+
+ stdout, stderr, retcode = self.qemu_command('systemctl --no-pager show --property=ExecMainStatus aktualizr')
+ self.assertIn(b'ExecMainStatus=0', stdout, 'Aktualizr did not restart')
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64_ptest.py b/external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64_ptest.py
new file mode 100644
index 00000000..d20a9f0e
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/updater_qemux86_64_ptest.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0111,C0325
+import re
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd
+from testutils import metadir, qemu_launch, qemu_send_command, qemu_terminate
+
+
+class PtestTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ layer = "meta-updater-qemux86-64"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer, result.output) is None:
+ self.meta_qemu = metadir() + layer
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu)
+ else:
+ self.meta_qemu = None
+ self.append_config('MACHINE = "qemux86-64"')
+ self.append_config('SYSTEMD_AUTO_ENABLE_aktualizr = "disable"')
+ self.append_config('PTEST_ENABLED_pn-aktualizr = "1"')
+ self.append_config('IMAGE_INSTALL_append += "aktualizr-ptest ptest-runner "')
+ self.append_config('IMAGE_FSTYPES_remove = "ostreepush garagesign garagecheck"')
+ self.qemu, self.s = qemu_launch(machine='qemux86-64', mem="768M")
+
+ def tearDownLocal(self):
+ qemu_terminate(self.s)
+ if self.meta_qemu:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True)
+
+ def qemu_command(self, command, timeout=60):
+ return qemu_send_command(self.qemu.ssh_port, command, timeout=timeout)
+
+ def test_run_ptests(self):
+ # simulate a login shell, so that /usr/sbin is in $PATH (from /etc/profile)
+ stdout, stderr, retcode = self.qemu_command('sh -l -c ptest-runner', timeout=None)
+ output = stdout.decode()
+ print(output)
+
+ has_failure = re.search('^FAIL', output, flags=re.MULTILINE) is not None
+ if has_failure:
+ print("Full test suite log:")
+ stdout, _, _ = self.qemu_command('cat /tmp/aktualizr-ptest.log || cat /tmp/aktualizr-ptest.log.tmp', timeout=None)
+ print(stdout.decode(errors='replace'))
+
+ self.assertEqual(retcode, 0)
+ self.assertFalse(has_failure)
diff --git a/external/meta-updater/lib/oeqa/selftest/cases/updater_raspberrypi.py b/external/meta-updater/lib/oeqa/selftest/cases/updater_raspberrypi.py
new file mode 100644
index 00000000..26d5c4c6
--- /dev/null
+++ b/external/meta-updater/lib/oeqa/selftest/cases/updater_raspberrypi.py
@@ -0,0 +1,82 @@
+# pylint: disable=C0111,C0325
+import os
+import logging
+import re
+import unittest
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+
+from testutils import metadir
+
+
+class RpiTests(OESelftestTestCase):
+
+ def setUpLocal(self):
+ # Add layers before changing the machine type, otherwise the sanity
+ # checker complains loudly.
+ layer_python = "meta-openembedded/meta-python"
+ layer_rpi = "meta-raspberrypi"
+ layer_upd_rpi = "meta-updater-raspberrypi"
+ result = runCmd('bitbake-layers show-layers')
+ if re.search(layer_python, result.output) is None:
+ self.meta_python = metadir() + layer_python
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_python)
+ else:
+ self.meta_python = None
+ if re.search(layer_rpi, result.output) is None:
+ self.meta_rpi = metadir() + layer_rpi
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_rpi)
+ else:
+ self.meta_rpi = None
+ if re.search(layer_upd_rpi, result.output) is None:
+ self.meta_upd_rpi = metadir() + layer_upd_rpi
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_upd_rpi)
+ else:
+ self.meta_upd_rpi = None
+
+ # This is trickier that I would've thought. The fundamental problem is
+ # that the qemu layer changes the u-boot file extension to .rom, but
+ # raspberrypi still expects .bin. To prevent this, the qemu layer must
+ # be temporarily removed if it is present. It has to be removed by name
+ # without the complete path, but to add it back when we are done, we
+ # need the full path.
+ p = re.compile(r'meta-updater-qemux86-64\s*(\S*meta-updater-qemux86-64)\s')
+ m = p.search(result.output)
+ if m and m.lastindex > 0:
+ self.meta_qemu = m.group(1)
+ runCmd('bitbake-layers remove-layer meta-updater-qemux86-64')
+ else:
+ self.meta_qemu = None
+
+ self.append_config('MACHINE = "raspberrypi3"')
+ self.append_config('SOTA_CLIENT_PROV = " aktualizr-shared-prov "')
+
+ def tearDownLocal(self):
+ if self.meta_qemu:
+ runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu, ignore_status=True)
+ if self.meta_upd_rpi:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_upd_rpi, ignore_status=True)
+ if self.meta_rpi:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_rpi, ignore_status=True)
+ if self.meta_python:
+ runCmd('bitbake-layers remove-layer "%s"' % self.meta_python, ignore_status=True)
+
+ def test_build(self):
+ logger = logging.getLogger("selftest")
+ logger.info('Running bitbake to build core-image-minimal')
+ bitbake('core-image-minimal')
+ credentials = get_bb_var('SOTA_PACKED_CREDENTIALS')
+ # Skip the test if the variable SOTA_PACKED_CREDENTIALS is not set.
+ if credentials is None:
+ raise unittest.SkipTest("Variable 'SOTA_PACKED_CREDENTIALS' not set.")
+ # Check if the file exists.
+ self.assertTrue(os.path.isfile(credentials), "File %s does not exist" % credentials)
+ deploydir = get_bb_var('DEPLOY_DIR_IMAGE')
+ imagename = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
+ # Check if the credentials are included in the output image.
+ result = runCmd('tar -jtvf %s/%s.tar.bz2 | grep sota_provisioning_credentials.zip' %
+ (deploydir, imagename), ignore_status=True)
+ self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-connectivity/connman/connman_%.bbappend b/external/meta-updater/recipes-connectivity/connman/connman_%.bbappend
new file mode 100644
index 00000000..b3633cc9
--- /dev/null
+++ b/external/meta-updater/recipes-connectivity/connman/connman_%.bbappend
@@ -0,0 +1 @@
+RPROVIDES_${PN} += "virtual/network-configuration"
diff --git a/external/meta-updater/recipes-connectivity/networkd-dhcp-conf/files/20-wired-dhcp.network b/external/meta-updater/recipes-connectivity/networkd-dhcp-conf/files/20-wired-dhcp.network
new file mode 100644
index 00000000..edb36788
--- /dev/null
+++ b/external/meta-updater/recipes-connectivity/networkd-dhcp-conf/files/20-wired-dhcp.network
@@ -0,0 +1,5 @@
+[Match]
+@MATCH_DIRECTIVE@
+
+[Network]
+DHCP=yes
diff --git a/external/meta-updater/recipes-connectivity/networkd-dhcp-conf/networkd-dhcp-conf.bb b/external/meta-updater/recipes-connectivity/networkd-dhcp-conf/networkd-dhcp-conf.bb
new file mode 100644
index 00000000..0700ac6e
--- /dev/null
+++ b/external/meta-updater/recipes-connectivity/networkd-dhcp-conf/networkd-dhcp-conf.bb
@@ -0,0 +1,28 @@
+SUMMARY = "systemd-networkd config to setup wired interface with dhcp"
+DESCRIPTION = "Provides automatic dhcp network configuration for wired \
+interfaces through systemd-networkd"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch systemd
+
+RPROVIDES_${PN} = "virtual/network-configuration"
+
+SRC_URI_append = " file://20-wired-dhcp.network"
+PR = "r1"
+
+RDEPENDS_${PN} = "systemd"
+
+S = "${WORKDIR}"
+
+PACKAGE_ARCH = "${MACHINE_ARCH}"
+
+FILES_${PN} = "${systemd_unitdir}/network/*"
+
+DEV_MATCH_DIRECTIVE ?= "Name=en*"
+
+do_install() {
+ install -d ${D}/${systemd_unitdir}/network
+ install -m 0644 ${WORKDIR}/20-wired-dhcp.network ${D}/${systemd_unitdir}/network
+ sed -i -e 's|@MATCH_DIRECTIVE@|${DEV_MATCH_DIRECTIVE}|g' ${D}${systemd_unitdir}/network/20-wired-dhcp.network
+}
diff --git a/external/meta-updater/recipes-core/images/initramfs-ostree-image.bb b/external/meta-updater/recipes-core/images/initramfs-ostree-image.bb
new file mode 100644
index 00000000..e77499e2
--- /dev/null
+++ b/external/meta-updater/recipes-core/images/initramfs-ostree-image.bb
@@ -0,0 +1,29 @@
+# Netboot initramfs image.
+DESCRIPTION = "OSTree initramfs image"
+
+PACKAGE_INSTALL = "ostree-switchroot ostree-initrd busybox base-passwd ${ROOTFS_BOOTSTRAP_INSTALL}"
+
+SYSTEMD_DEFAULT_TARGET = "initrd.target"
+
+# Do not pollute the initrd image with rootfs features
+IMAGE_FEATURES = ""
+
+export IMAGE_BASENAME = "initramfs-ostree-image"
+IMAGE_LINGUAS = ""
+
+LICENSE = "MIT"
+
+IMAGE_FSTYPES = "${INITRAMFS_FSTYPES}"
+
+inherit core-image
+
+IMAGE_ROOTFS_SIZE = "8192"
+
+# Users will often ask for extra space in their rootfs by setting this
+# globally. Since this is a initramfs, we don't want to make it bigger
+IMAGE_ROOTFS_EXTRA_SPACE = "0"
+IMAGE_OVERHEAD_FACTOR = "1.0"
+
+BAD_RECOMMENDATIONS += "busybox-syslog"
+
+
diff --git a/external/meta-updater/recipes-devtools/valgrind/files/bug344802-unhandled-0xec510f1e.patch b/external/meta-updater/recipes-devtools/valgrind/files/bug344802-unhandled-0xec510f1e.patch
new file mode 100644
index 00000000..a25f5416
--- /dev/null
+++ b/external/meta-updater/recipes-devtools/valgrind/files/bug344802-unhandled-0xec510f1e.patch
@@ -0,0 +1,250 @@
+diff --git a/VEX/priv/guest_arm_defs.h b/VEX/priv/guest_arm_defs.h
+index 2ccbe4398..90312fbd4 100644
+--- a/VEX/priv/guest_arm_defs.h
++++ b/VEX/priv/guest_arm_defs.h
+@@ -350,6 +350,10 @@ typedef
+ }
+ ARMCondcode;
+
++extern UInt arm_dirtyhelper_MRS_CNTFRQ ( void );
++extern ULong arm_dirtyhelper_MRRS_CNTVCT ( void );
++extern ULong arm_dirtyhelper_MRRS_CNTPCT ( void );
++
+ #endif /* ndef __VEX_GUEST_ARM_DEFS_H */
+
+ /*---------------------------------------------------------------*/
+diff --git a/VEX/priv/guest_arm_helpers.c b/VEX/priv/guest_arm_helpers.c
+index 8a028736e..89b17ce7b 100644
+--- a/VEX/priv/guest_arm_helpers.c
++++ b/VEX/priv/guest_arm_helpers.c
+@@ -1445,6 +1445,53 @@ VexGuestLayout
+ };
+
+
++UInt arm_dirtyhelper_MRS_CNTFRQ ( void )
++{
++#if __ARM_ARCH_ISA_ARM //{
++ UInt w = 0x55555555UL; /* overwritten */
++ __asm__ __volatile__("mrc p15, 0, %0, c14, c0, 0" : "=r"(w));
++ return w;
++#elif __ARM_ARCH_ISA_A64 //}{
++ UInt w;
++ __asm__ __volatile__("mrs %0,cntfrq_el0": "=r"(w));
++ return w;
++#else //}{
++ return 0;
++#endif //}
++}
++
++ULong arm_dirtyhelper_MRRS_CNTVCT ( void )
++{
++#if __ARM_ARCH_ISA_ARM //}{
++ UInt w0;
++ UInt w1;
++ __asm__ __volatile__("mrrc p15, 1, %0, %1, c14" : "=r"(w0), "=r"(w1));
++ return (((ULong)w1)<<32) | w0;
++#elif __ARM_ARCH_ISA_A64 //{
++ ULong w;
++ __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(w));
++ return w;
++#else //}{
++ return 0;
++#endif //}
++}
++
++ULong arm_dirtyhelper_MRRS_CNTPCT ( void )
++{
++#if __ARM_ARCH_ISA_ARM //}{
++ UInt w0;
++ UInt w1;
++ __asm__ __volatile__("mrrc p15, 0, %0, %1, c14" : "=r"(w0), "=r"(w1));
++ return (((ULong)w1)<<32) | w0;
++#elif __ARM_ARCH_ISA_A64 //{
++ ULong w;
++ __asm__ __volatile__("mrs %0, cntpct_el0" : "=r"(w));
++ return w;
++#else //}{
++ return 0;
++#endif //}
++}
++
+ /*---------------------------------------------------------------*/
+ /*--- end guest_arm_helpers.c ---*/
+ /*---------------------------------------------------------------*/
+diff --git a/VEX/priv/guest_arm_toIR.c b/VEX/priv/guest_arm_toIR.c
+index d858c85e0..f96af92c4 100644
+--- a/VEX/priv/guest_arm_toIR.c
++++ b/VEX/priv/guest_arm_toIR.c
+@@ -18755,6 +18755,87 @@ DisResult disInstr_ARM_WRK (
+ /* fall through */
+ }
+
++ /* CNTFRQ: mrc p15, 0, rX, c14, c0, 0 */
++ if (0x0e1e0f10 == (insn & 0x0FFF0FFF)) {
++ UInt rD = INSN(15,12);
++ if (rD <= 14) {
++ /* skip r15, that's too stupid to handle */
++ IRTemp val = newTemp(Ity_I32);
++ IRExpr** args = mkIRExprVec_0();
++ IRDirty* d = unsafeIRDirty_1_N(
++ val,
++ 0/*regparms*/,
++ "arm_dirtyhelper_MRS_CNTFRQ",
++ &arm_dirtyhelper_MRS_CNTFRQ,
++ args
++ );
++ /* execute the dirty call, dumping the result in val. */
++ stmt( IRStmt_Dirty(d) );
++ putIRegA(rD, mkexpr(val), condT, Ijk_Boring);
++ DIP("mrc%s p15, 0, r%u, c14, c0, 0\n", nCC(INSN_COND), rD);
++ goto decode_success;
++ }
++ /* fall through */
++ }
++
++ /* CNTPCT */
++ if (0x0c500f0e == (insn & 0x0FF00FFF)) {
++ UInt rDhi = INSN(19,16);
++ UInt rDlo = INSN(15,12);
++ if (rDhi <= 14 && rDlo <= 14) {
++ /* skip r15, that's too stupid to handle */
++ IRTemp resHi = newTemp(Ity_I32);
++ IRTemp resLo = newTemp(Ity_I32);
++ IRTemp val = newTemp(Ity_I64);
++ IRExpr** args = mkIRExprVec_0();
++ IRDirty* d = unsafeIRDirty_1_N(
++ val,
++ 0/*regparms*/,
++ "arm_dirtyhelper_MRRS_CNTPCT",
++ &arm_dirtyhelper_MRRS_CNTPCT,
++ args
++ );
++ /* execute the dirty call, dumping the result in val. */
++ stmt( IRStmt_Dirty(d) );
++ assign( resHi, unop(Iop_64HIto32, mkexpr(val)) );
++ assign( resLo, unop(Iop_64to32, mkexpr(val)) );
++ putIRegA( rDhi, mkexpr(resHi), condT, Ijk_Boring );
++ putIRegA( rDlo, mkexpr(resLo), condT, Ijk_Boring );
++ DIP("mrrc%s p15, 0, r%u, r%u, c14\n", nCC(INSN_COND), rDlo, rDhi);
++ goto decode_success;
++ }
++ /* fall through */
++ }
++
++ /* CNTVCT */
++ if (0x0c500f1e == (insn & 0x0FF00FFF)) {
++ UInt rDhi = INSN(19,16);
++ UInt rDlo = INSN(15,12);
++ if (rDhi <= 14 && rDlo <= 14) {
++ /* skip r15, that's too stupid to handle */
++ IRTemp resHi = newTemp(Ity_I32);
++ IRTemp resLo = newTemp(Ity_I32);
++ IRTemp val = newTemp(Ity_I64);
++ IRExpr** args = mkIRExprVec_0();
++ IRDirty* d = unsafeIRDirty_1_N(
++ val,
++ 0/*regparms*/,
++ "arm_dirtyhelper_MRRS_CNTVCT",
++ &arm_dirtyhelper_MRRS_CNTVCT,
++ args
++ );
++ /* execute the dirty call, dumping the result in val. */
++ stmt( IRStmt_Dirty(d) );
++ assign( resHi, unop(Iop_64HIto32, mkexpr(val)) );
++ assign( resLo, unop(Iop_64to32, mkexpr(val)) );
++ putIRegA( rDhi, mkexpr(resHi), condT, Ijk_Boring );
++ putIRegA( rDlo, mkexpr(resLo), condT, Ijk_Boring );
++ DIP("mrrc%s p15, 1, r%u, r%u, c14\n", nCC(INSN_COND), rDlo, rDhi);
++ goto decode_success;
++ }
++ /* fall through */
++ }
++
+ /* Handle various kinds of barriers. This is rather indiscriminate
+ in the sense that they are all turned into an IR Fence, which
+ means we don't know which they are, so the back end has to
+@@ -23196,6 +23277,84 @@ DisResult disInstr_THUMB_WRK (
+ /* fall through */
+ }
+
++ /* CNTFRQ: mrc p15, 0, rX, c14, c0, 0 */
++ if ((INSN0(15,0) == 0xee1e) && (INSN1(11,0) == 0xf10)) {
++ UInt rD = INSN1(15,12);
++ if (!isBadRegT(rD)) {
++ IRTemp val = newTemp(Ity_I32);
++ IRExpr** args = mkIRExprVec_0();
++ IRDirty* d = unsafeIRDirty_1_N(
++ val,
++ 0/*regparms*/,
++ "arm_dirtyhelper_MRS_CNTFRQ",
++ &arm_dirtyhelper_MRS_CNTFRQ,
++ args
++ );
++ /* execute the dirty call, dumping the result in val. */
++ stmt( IRStmt_Dirty(d) );
++ putIRegT(rD, mkexpr(val), condT);
++ DIP("mrc p15, 0, r%u, c14, c0, 0\n", rD);
++ goto decode_success;
++ }
++ /* fall through */
++ }
++
++ /* CNTPCT */
++ if ((INSN0(15,4) == 0xec5) && (INSN1(11,0) == 0xf0e)) {
++ UInt rDhi = INSN0(3,0);
++ UInt rDlo = INSN1(15,12);
++ if (!isBadRegT(rDhi) && !isBadRegT(rDlo)) {
++ IRTemp resHi = newTemp(Ity_I32);
++ IRTemp resLo = newTemp(Ity_I32);
++ IRTemp val = newTemp(Ity_I64);
++ IRExpr** args = mkIRExprVec_0();
++ IRDirty* d = unsafeIRDirty_1_N(
++ val,
++ 0/*regparms*/,
++ "arm_dirtyhelper_MRRS_CNTPCT",
++ &arm_dirtyhelper_MRRS_CNTPCT,
++ args
++ );
++ /* execute the dirty call, dumping the result in val. */
++ stmt( IRStmt_Dirty(d) );
++ assign( resHi, unop(Iop_64HIto32, mkexpr(val)) );
++ assign( resLo, unop(Iop_64to32, mkexpr(val)) );
++ putIRegT( rDhi, mkexpr(resHi), condT );
++ putIRegT( rDlo, mkexpr(resLo), condT );
++ DIP("mrrc p15, 0, r%u, r%u, c14\n", rDlo, rDhi);
++ goto decode_success;
++ }
++ /* fall through */
++ }
++
++ /* CNTVCT */
++ if ((INSN0(15,4) == 0xec5) && (INSN1(11,0) == 0xf1e)) {
++ UInt rDhi = INSN0(3,0);
++ UInt rDlo = INSN1(15,12);
++ if (!isBadRegT(rDhi) && !isBadRegT(rDlo)) {
++ IRTemp resHi = newTemp(Ity_I32);
++ IRTemp resLo = newTemp(Ity_I32);
++ IRTemp val = newTemp(Ity_I64);
++ IRExpr** args = mkIRExprVec_0();
++ IRDirty* d = unsafeIRDirty_1_N(
++ val,
++ 0/*regparms*/,
++ "arm_dirtyhelper_MRRS_CNTVCT",
++ &arm_dirtyhelper_MRRS_CNTVCT,
++ args
++ );
++ /* execute the dirty call, dumping the result in val. */
++ stmt( IRStmt_Dirty(d) );
++ assign( resHi, unop(Iop_64HIto32, mkexpr(val)) );
++ assign( resLo, unop(Iop_64to32, mkexpr(val)) );
++ putIRegT( rDhi, mkexpr(resHi), condT );
++ putIRegT( rDlo, mkexpr(resLo), condT );
++ DIP("mrrc p15, 1, r%u, r%u, c14\n", rDlo, rDhi);
++ goto decode_success;
++ }
++ /* fall through */
++ }
++
+ /* ------------------- CLREX ------------------ */
+ if (INSN0(15,0) == 0xF3BF && INSN1(15,0) == 0x8F2F) {
+ /* AFAICS, this simply cancels a (all?) reservations made by a
diff --git a/external/meta-updater/recipes-devtools/valgrind/valgrind_%.bbappend b/external/meta-updater/recipes-devtools/valgrind/valgrind_%.bbappend
new file mode 100644
index 00000000..c5f7c314
--- /dev/null
+++ b/external/meta-updater/recipes-devtools/valgrind/valgrind_%.bbappend
@@ -0,0 +1,4 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
+
+# from https://bugs.kde.org/show_bug.cgi?id=344802 (John Reiser)
+SRC_URI += "file://bug344802-unhandled-0xec510f1e.patch"
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-creds.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-creds.bb
new file mode 100644
index 00000000..6e02a501
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-creds.bb
@@ -0,0 +1,60 @@
+SUMMARY = "Credentials for device provisioning with fleet CA certificate"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+# WARNING: it is NOT a production solution. The secure way to provision devices
+# is to create certificate request directly on the device (either with HSM/TPM
+# or with software) and then sign it with a CA stored on a disconnected machine.
+
+DEPENDS = "aktualizr aktualizr-native"
+ALLOW_EMPTY_${PN} = "1"
+
+SRC_URI = " \
+ file://ca.cnf \
+ "
+
+require credentials.inc
+
+export SOTA_CACERT_PATH
+export SOTA_CAKEY_PATH
+
+do_install() {
+ if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
+ if [ -z ${SOTA_CACERT_PATH} ]; then
+ SOTA_CACERT_PATH=${DEPLOY_DIR_IMAGE}/CA/cacert.pem
+ SOTA_CAKEY_PATH=${DEPLOY_DIR_IMAGE}/CA/ca.private.pem
+ mkdir -p ${DEPLOY_DIR_IMAGE}/CA
+ bbwarn "SOTA_CACERT_PATH is not specified, use default one at ${SOTA_CACERT_PATH}"
+
+ if [ ! -f ${SOTA_CACERT_PATH} ]; then
+ bbwarn "${SOTA_CACERT_PATH} does not exist, generate a new CA"
+ SOTA_CACERT_DIR_PATH="$(dirname "${SOTA_CACERT_PATH}")"
+ openssl genrsa -out ${SOTA_CACERT_DIR_PATH}/ca.private.pem 4096
+ openssl req -key ${SOTA_CACERT_DIR_PATH}/ca.private.pem -new -x509 -days 7300 -out ${SOTA_CACERT_PATH} -subj "/C=DE/ST=Berlin/O=Reis und Kichererbsen e.V/commonName=meta-updater" -batch -config ${WORKDIR}/ca.cnf -extensions cacert
+ bbwarn "${SOTA_CACERT_PATH} has been created, you'll need to upload it to the server"
+ fi
+ fi
+
+ if [ -z ${SOTA_CAKEY_PATH} ]; then
+ bbfatal "SOTA_CAKEY_PATH should be set when using device credential provisioning"
+ fi
+
+ install -m 0700 -d ${D}${localstatedir}/sota
+ aktualizr-cert-provider --credentials ${SOTA_PACKED_CREDENTIALS} \
+ --fleet-ca ${SOTA_CACERT_PATH} \
+ --fleet-ca-key ${SOTA_CAKEY_PATH} \
+ --root-ca \
+ --server-url \
+ --local ${D} \
+ --config ${STAGING_DIR_HOST}${libdir}/sota/sota-device-cred.toml
+ fi
+}
+
+FILES_${PN} = " \
+ ${localstatedir}/sota/*"
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-hsm.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-hsm.bb
new file mode 100644
index 00000000..c3cd593b
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov-hsm.bb
@@ -0,0 +1,30 @@
+SUMMARY = "Aktualizr configuration for device credential provisioning with HSM support"
+DESCRIPTION = "Configuration for provisioning Aktualizr with device credentials using externally provided or generated CA with HSM support"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+DEPENDS = "aktualizr aktualizr-native"
+RDEPENDS_${PN}_append = "${@' aktualizr-device-prov-creds softhsm-testtoken' if d.getVar('SOTA_DEPLOY_CREDENTIALS') == '1' else ''}"
+
+SRC_URI = ""
+PV = "1.0"
+PR = "6"
+
+require credentials.inc
+
+do_install() {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${STAGING_DIR_HOST}${libdir}/sota/sota-device-cred-hsm.toml \
+ ${D}${libdir}/sota/conf.d/20-sota-device-cred-hsm.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d \
+ ${libdir}/sota/conf.d/20-sota-device-cred-hsm.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov.bb
new file mode 100644
index 00000000..d5795324
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr-device-prov.bb
@@ -0,0 +1,29 @@
+SUMMARY = "Aktualizr configuration for device credential provisioning"
+DESCRIPTION = "Configuration for provisioning Aktualizr with device credentials using externally provided or generated CA"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+DEPENDS = "aktualizr aktualizr-native openssl-native"
+RDEPENDS_${PN}_append = "${@' aktualizr-device-prov-creds' if d.getVar('SOTA_DEPLOY_CREDENTIALS') == '1' else ''}"
+
+PV = "1.0"
+PR = "1"
+
+require credentials.inc
+
+do_install() {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${STAGING_DIR_HOST}${libdir}/sota/sota-device-cred.toml \
+ ${D}${libdir}/sota/conf.d/20-sota-device-cred.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d \
+ ${libdir}/sota/conf.d/20-sota-device-cred.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov-creds.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov-creds.bb
new file mode 100644
index 00000000..dbb5fde5
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov-creds.bb
@@ -0,0 +1,32 @@
+SUMMARY = "Credentials for shared provisioning"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+DEPENDS = "aktualizr-native zip-native"
+ALLOW_EMPTY_${PN} = "1"
+
+require credentials.inc
+
+do_install() {
+ if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
+ install -m 0700 -d ${D}${localstatedir}/sota
+ cp "${SOTA_PACKED_CREDENTIALS}" ${D}${localstatedir}/sota/sota_provisioning_credentials.zip
+ # Device should not be able to push data to treehub
+ zip -d ${D}${localstatedir}/sota/sota_provisioning_credentials.zip treehub.json
+ # Device has no use for the API Gateway. Remove if present. See:
+ # https://github.com/advancedtelematic/ota-plus-server/pull/1913/
+ if unzip -l ${D}${localstatedir}/sota/sota_provisioning_credentials.zip api_gateway.url > /dev/null; then
+ zip -d ${D}${localstatedir}/sota/sota_provisioning_credentials.zip api_gateway.url
+ fi
+ fi
+}
+
+FILES_${PN} = " \
+ ${localstatedir}/sota/sota_provisioning_credentials.zip \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov.bb
new file mode 100644
index 00000000..d3d6f165
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr-shared-prov.bb
@@ -0,0 +1,43 @@
+SUMMARY = "Aktualizr configuration for shared credential provisioning"
+DESCRIPTION = "Configuration for provisioning Aktualizr with shared credentials"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+DEPENDS = "aktualizr-native zip-native"
+RDEPENDS_${PN}_append = "${@' aktualizr-shared-prov-creds' if d.getVar('SOTA_DEPLOY_CREDENTIALS') == '1' else ''}"
+PV = "1.0"
+PR = "6"
+
+SRC_URI = ""
+
+require credentials.inc
+
+do_install() {
+ if [ -n "${SOTA_AUTOPROVISION_CREDENTIALS}" ]; then
+ bbwarn "SOTA_AUTOPROVISION_CREDENTIALS are ignored. Please use SOTA_PACKED_CREDENTIALS"
+ fi
+ if [ -n "${SOTA_AUTOPROVISION_URL}" ]; then
+ bbwarn "SOTA_AUTOPROVISION_URL is ignored. Please use SOTA_PACKED_CREDENTIALS"
+ fi
+ if [ -n "${SOTA_AUTOPROVISION_URL_FILE}" ]; then
+ bbwarn "SOTA_AUTOPROVISION_URL_FILE is ignored. Please use SOTA_PACKED_CREDENTIALS"
+ fi
+ if [ -n "${OSTREE_PUSH_CREDENTIALS}" ]; then
+ bbwarn "OSTREE_PUSH_CREDENTIALS is ignored. Please use SOTA_PACKED_CREDENTIALS"
+ fi
+
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${STAGING_DIR_NATIVE}${libdir}/sota/sota-shared-cred.toml \
+ ${D}${libdir}/sota/conf.d/20-sota-shared-cred.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d \
+ ${libdir}/sota/conf.d/20-sota-shared-cred.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr-uboot-env-rollback.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr-uboot-env-rollback.bb
new file mode 100644
index 00000000..860f225c
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr-uboot-env-rollback.bb
@@ -0,0 +1,24 @@
+SUMMARY = "Aktualizr configuration snippet to enable uboot bootcount function"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+DEPENDS = "aktualizr-native"
+RDEPENDS_${PN} = "aktualizr"
+
+SRC_URI = ""
+
+do_install() {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${STAGING_DIR_NATIVE}${libdir}/sota/sota-uboot-env.toml ${D}${libdir}/sota/conf.d/30-rollback.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d \
+ ${libdir}/sota/conf.d/30-rollback.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/aktualizr_git.bb b/external/meta-updater/recipes-sota/aktualizr/aktualizr_git.bb
new file mode 100644
index 00000000..5de341e4
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/aktualizr_git.bb
@@ -0,0 +1,171 @@
+SUMMARY = "Aktualizr SOTA Client"
+DESCRIPTION = "SOTA Client application written in C++"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=9741c346eef56131163e13b9db1241b3"
+
+DEPENDS = "boost curl openssl libarchive libsodium sqlite3 asn1c-native"
+DEPENDS_append = "${@bb.utils.contains('PTEST_ENABLED', '1', ' coreutils-native net-tools-native ostree-native aktualizr-native ', '', d)}"
+RDEPENDS_${PN}_class-target = "aktualizr-configs lshw"
+RDEPENDS_${PN}-host-tools = "aktualizr aktualizr-repo aktualizr-cert-provider ${@bb.utils.contains('PACKAGECONFIG', 'sota-tools', 'garage-deploy garage-push', '', d)}"
+
+RDEPENDS_${PN}-ptest += "bash cmake curl python3-misc python3-modules openssl-bin sqlite3 valgrind"
+
+PV = "1.0+git${SRCPV}"
+PR = "7"
+
+GARAGE_SIGN_PV = "0.7.0-3-gf5ba640"
+
+SRC_URI = " \
+ gitsm://github.com/advancedtelematic/aktualizr;branch=${BRANCH} \
+ file://run-ptest \
+ file://aktualizr.service \
+ file://aktualizr-secondary.service \
+ file://aktualizr-serialcan.service \
+ file://10-resource-control.conf \
+ ${@ d.expand("https://ats-tuf-cli-releases.s3-eu-central-1.amazonaws.com/cli-${GARAGE_SIGN_PV}.tgz;unpack=0") if d.getVar('GARAGE_SIGN_AUTOVERSION') != '1' else ''} \
+ "
+
+# for garage-sign archive
+SRC_URI[md5sum] = "e104ccd4f32e52571a5fc0e5042db050"
+SRC_URI[sha256sum] = "c590be1a57523bfe097af82279eda5c97cf40ae47fb27162cf33c469702c8a9b"
+
+SRCREV = "9c592cf9d8dfcd995d47753f2be7bd1a2b56c7da"
+BRANCH ?= "master"
+
+S = "${WORKDIR}/git"
+
+inherit cmake pkgconfig ptest systemd
+
+# disable ptest by default as it slows down builds quite a lot
+# can be enabled manually by setting 'PTEST_ENABLED_pn-aktualizr' to '1' in local.conf
+PTEST_ENABLED = "0"
+
+SYSTEMD_PACKAGES = "${PN} ${PN}-secondary"
+SYSTEMD_SERVICE_${PN} = "aktualizr.service"
+SYSTEMD_SERVICE_${PN}-secondary = "aktualizr-secondary.service"
+
+EXTRA_OECMAKE = "-DCMAKE_BUILD_TYPE=Release -DAKTUALIZR_VERSION=${PV} ${@bb.utils.contains('PTEST_ENABLED', '1', '-DTESTSUITE_VALGRIND=on', '', d)}"
+
+GARAGE_SIGN_OPS = "${@ d.expand('-DGARAGE_SIGN_ARCHIVE=${WORKDIR}/cli-${GARAGE_SIGN_PV}.tgz') if d.getVar('GARAGE_SIGN_AUTOVERSION') != '1' else ''}"
+
+PACKAGECONFIG ?= "ostree ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} ${@bb.utils.filter('SOTA_CLIENT_FEATURES', 'hsm serialcan ubootenv', d)}"
+PACKAGECONFIG_class-native = "sota-tools"
+PACKAGECONFIG[warning-as-error] = "-DWARNING_AS_ERROR=ON,-DWARNING_AS_ERROR=OFF,"
+PACKAGECONFIG[ostree] = "-DBUILD_OSTREE=ON,-DBUILD_OSTREE=OFF,ostree,"
+PACKAGECONFIG[hsm] = "-DBUILD_P11=ON,-DBUILD_P11=OFF,libp11,"
+PACKAGECONFIG[sota-tools] = "-DBUILD_SOTA_TOOLS=ON ${GARAGE_SIGN_OPS},-DBUILD_SOTA_TOOLS=OFF,glib-2.0,"
+PACKAGECONFIG[systemd] = "-DBUILD_SYSTEMD=ON,-DBUILD_SYSTEMD=OFF,systemd,"
+PACKAGECONFIG[load-tests] = "-DBUILD_LOAD_TESTS=ON,-DBUILD_LOAD_TESTS=OFF,"
+PACKAGECONFIG[serialcan] = ",,,slcand-start"
+PACKAGECONFIG[ubootenv] = ",,,u-boot-fw-utils aktualizr-uboot-env-rollback"
+
+# can be overriden in configuration with `RESOURCE_xxx_pn-aktualizr`
+# see `man systemd.resource-control` for details
+
+# can be used to lower aktualizr priority, default is 100
+RESOURCE_CPU_WEIGHT = "100"
+# will be slowed down when it reaches 'high', killed when it reaches 'max'
+RESOURCE_MEMORY_HIGH = "100M"
+RESOURCE_MEMORY_MAX = "80%"
+
+do_compile_ptest() {
+ cmake_runcmake_build --target build_tests "${PARALLEL_MAKE}"
+}
+
+do_install_ptest() {
+ # copy the complete source directory (contains build)
+ cp -r ${B}/ ${D}/${PTEST_PATH}/build
+ cp -r ${S}/ ${D}/${PTEST_PATH}/src
+
+ # remove huge external unused repository
+ rm -rf ${D}/${PTEST_PATH}/src/partial/extern/RIOT
+
+ # remove huge build artifacts
+ find ${D}/${PTEST_PATH}/build/src -name "*.a" -delete
+
+ # fix the absolute paths
+ find ${D}/${PTEST_PATH}/build -name "CMakeFiles" | xargs rm -rf
+ find ${D}/${PTEST_PATH}/build -name "*.cmake" -or -name "DartConfiguration.tcl" -or -name "run-valgrind" | xargs sed -e "s|${S}|${PTEST_PATH}/src|g" -e "s|${B}|${PTEST_PATH}/build|g" -e "s|\"--gtest_output[^\"]*\"||g" -i
+}
+
+do_install_append () {
+ install -d ${D}${libdir}/sota
+ install -m 0644 ${S}/config/sota-shared-cred.toml ${D}/${libdir}/sota/sota-shared-cred.toml
+ install -m 0644 ${S}/config/sota-device-cred-hsm.toml ${D}/${libdir}/sota/sota-device-cred-hsm.toml
+ install -m 0644 ${S}/config/sota-device-cred.toml ${D}/${libdir}/sota/sota-device-cred.toml
+ install -m 0644 ${S}/config/sota-secondary.toml ${D}/${libdir}/sota/sota-secondary.toml
+ install -m 0644 ${S}/config/sota-uboot-env.toml ${D}/${libdir}/sota/sota-uboot-env.toml
+ install -d ${D}${systemd_unitdir}/system
+ install -m 0644 ${WORKDIR}/aktualizr-secondary.service ${D}${systemd_unitdir}/system/aktualizr-secondary.service
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0700 -d ${D}${sysconfdir}/sota/conf.d
+
+ if [ -n "${SOTA_HARDWARE_ID}" ]; then
+ printf "[provision]\nprimary_ecu_hardware_id = ${SOTA_HARDWARE_ID}\n" > ${D}${libdir}/sota/conf.d/40-hardware-id.toml
+ fi
+
+ install -m 0755 -d ${D}${systemd_unitdir}/system
+ aktualizr_service=${@bb.utils.contains('SOTA_CLIENT_FEATURES', 'serialcan', '${WORKDIR}/aktualizr-serialcan.service', '${WORKDIR}/aktualizr.service', d)}
+ install -m 0644 ${aktualizr_service} ${D}${systemd_unitdir}/system/aktualizr.service
+
+ if ${@bb.utils.contains('PACKAGECONFIG', 'sota-tools', 'true', 'false', d)}; then
+ install -m 0755 ${B}/src/sota_tools/garage-sign/bin/* ${D}${bindir}
+ install -m 0644 ${B}/src/sota_tools/garage-sign/lib/* ${D}${libdir}
+ fi
+
+ # resource control
+ install -d ${D}/${systemd_system_unitdir}/aktualizr.service.d
+ install -m 0644 ${WORKDIR}/10-resource-control.conf ${D}/${systemd_system_unitdir}/aktualizr.service.d
+
+ sed -i -e 's|@CPU_WEIGHT@|${RESOURCE_CPU_WEIGHT}|g' \
+ -e 's|@MEMORY_HIGH@|${RESOURCE_MEMORY_HIGH}|g' \
+ -e 's|@MEMORY_MAX@|${RESOURCE_MEMORY_MAX}|g' \
+ ${D}${systemd_system_unitdir}/aktualizr.service.d/10-resource-control.conf
+}
+
+PACKAGESPLITFUNCS_prepend = "split_hosttools_packages "
+
+python split_hosttools_packages () {
+ bindir = d.getVar('bindir')
+
+ # Split all binaries to their own packages except aktualizr-info,
+ # aktualizr-info should stay in main package aktualizr.
+ do_split_packages(d, bindir, r'^((?!(aktualizr-info)).*)$', '%s', 'Aktualizr tool - %s', extra_depends='aktualizr-configs', prepend=False)
+}
+
+PACKAGES_DYNAMIC = "^aktualizr-.* ^garage-.*"
+
+PACKAGES =+ "${PN}-resource-control ${PN}-examples ${PN}-secondary ${PN}-configs ${PN}-host-tools"
+
+ALLOW_EMPTY_${PN}-host-tools = "1"
+
+FILES_${PN} = " \
+ ${bindir}/aktualizr \
+ ${bindir}/aktualizr-info \
+ ${systemd_unitdir}/system/aktualizr.service \
+ "
+
+FILES_${PN}-resource-control = " \
+ ${systemd_system_unitdir}/aktualizr.service.d/10-resource-control.conf \
+ "
+
+FILES_${PN}-configs = " \
+ ${sysconfdir}/sota/* \
+ ${libdir}/sota/* \
+ "
+
+FILES_${PN}-examples = " \
+ ${bindir}/hmi-stub \
+ "
+
+FILES_${PN}-secondary = " \
+ ${bindir}/aktualizr-secondary \
+ ${libdir}/sota/sota-secondary.toml \
+ ${systemd_unitdir}/system/aktualizr-secondary.service \
+ "
+
+BBCLASSEXTEND = "native"
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/aktualizr/credentials.inc b/external/meta-updater/recipes-sota/aktualizr/credentials.inc
new file mode 100644
index 00000000..7c442573
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/credentials.inc
@@ -0,0 +1 @@
+SRC_URI_append = "${@('file://' + d.getVar('SOTA_PACKED_CREDENTIALS')) if d.getVar('SOTA_PACKED_CREDENTIALS') else ''}"
diff --git a/external/meta-updater/recipes-sota/aktualizr/files/10-resource-control.conf b/external/meta-updater/recipes-sota/aktualizr/files/10-resource-control.conf
new file mode 100644
index 00000000..254713c4
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/files/10-resource-control.conf
@@ -0,0 +1,6 @@
+[Service]
+CPUAccounting=true
+CPUWeight=@CPU_WEIGHT@
+MemoryAccounting=true
+MemoryHigh=@MEMORY_HIGH@
+MemoryMax=@MEMORY_MAX@
diff --git a/external/meta-updater/recipes-sota/aktualizr/files/aktualizr-secondary.service b/external/meta-updater/recipes-sota/aktualizr/files/aktualizr-secondary.service
new file mode 100644
index 00000000..b577ae8b
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/files/aktualizr-secondary.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Aktualizr SOTA Client (UPTANE Secondary)
+After=network.target
+
+[Service]
+RestartSec=10
+Restart=always
+ExecStart=/usr/bin/aktualizr-secondary
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/external/meta-updater/recipes-sota/aktualizr/files/aktualizr-serialcan.service b/external/meta-updater/recipes-sota/aktualizr/files/aktualizr-serialcan.service
new file mode 100644
index 00000000..b42f348e
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/files/aktualizr-serialcan.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=Aktualizr SOTA Client
+Wants=network-online.target slcand@ttyACM0.service
+After=network.target network-online.target slcand@ttyACM0.service
+
+Requires=network-online.target
+
+[Service]
+RestartSec=10
+Restart=always
+EnvironmentFile=/usr/lib/sota/sota.env
+ExecStart=/bin/sh -c "(ip addr | grep can0) && /usr/bin/aktualizr $AKTUALIZR_CMDLINE_PARAMETERS"
+
+[Install]
+WantedBy=multi-user.target
diff --git a/external/meta-updater/recipes-sota/aktualizr/files/aktualizr.service b/external/meta-updater/recipes-sota/aktualizr/files/aktualizr.service
new file mode 100644
index 00000000..726809e8
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/files/aktualizr.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Aktualizr SOTA Client
+After=network.target
+
+[Service]
+RestartSec=10
+Restart=always
+ExecStart=/usr/bin/aktualizr $AKTUALIZR_CMDLINE_PARAMETERS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/external/meta-updater/recipes-sota/aktualizr/files/ca.cnf b/external/meta-updater/recipes-sota/aktualizr/files/ca.cnf
new file mode 100644
index 00000000..352ec38c
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/files/ca.cnf
@@ -0,0 +1,10 @@
+[req]
+req_extensions = cacert
+distinguished_name = req_distinguished_name
+
+[req_distinguished_name]
+
+[cacert]
+basicConstraints = critical,CA:true
+keyUsage = keyCertSign
+
diff --git a/external/meta-updater/recipes-sota/aktualizr/files/run-ptest b/external/meta-updater/recipes-sota/aktualizr/files/run-ptest
new file mode 100755
index 00000000..ff441f90
--- /dev/null
+++ b/external/meta-updater/recipes-sota/aktualizr/files/run-ptest
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+set -eu
+
+AKTUALIZR_PTEST_PARALLEL_LEVEL=${AKTUALIZR_PTEST_PARALLEL_LEVEL:-2}
+
+filter_logs() {
+ awk '/^.*Test[[:space:]]*#[[:digit:]]+:/ {
+ a = gensub(/^.*Test[[:space:]]*#[[:digit:]]+:[[:space:]]*([^[:space:]]+).*(Passed|Skipped|Not Run|Failed|Timeout|Exception)[[:space:]:].*$/, "\\2: \\1", "g");
+ a = gensub(/^Passed/, "PASS", "g", a);
+ a = gensub(/^(Skipped|Disabled)/, "SKIP", "g", a);
+ a = gensub(/^(Not Run|Failed|Timeout|Exception)/, "FAIL", "g", a);
+ print a;
+ }'
+}
+
+cd build
+ctest -j "$AKTUALIZR_PTEST_PARALLEL_LEVEL" -O /tmp/aktualizr-ptest.log --output-on-failure -LE 'noptest' 2> /dev/null | filter_logs
diff --git a/external/meta-updater/recipes-sota/asn1c/asn1c.bb b/external/meta-updater/recipes-sota/asn1c/asn1c.bb
new file mode 100644
index 00000000..9d1517db
--- /dev/null
+++ b/external/meta-updater/recipes-sota/asn1c/asn1c.bb
@@ -0,0 +1,17 @@
+SUMMARY = "ASN.1 to C compiler"
+DESCRIPTION = "Generates serialization routines from ASN.1 schemas"
+HOMEPAGE = "http://lionet.info/asn1c"
+SECTION = "base"
+LICENSE = "BSD"
+LIC_FILES_CHKSUM = "file://LICENSE;md5=ee8bfaaa7d71cf3edb079475e6716d4b"
+
+inherit autotools native
+
+PV = "0.9.28"
+SRC_URI = "https://github.com/vlm/asn1c/releases/download/v${PV}/asn1c-${PV}.tar.gz \
+ file://skeletons_dir_fix.patch"
+SRC_URI[sha256sum] = "8007440b647ef2dd9fb73d931c33ac11764e6afb2437dbe638bb4e5fc82386b9"
+
+BBCLASSEXTEND = "native nativesdk"
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/asn1c/files/skeletons_dir_fix.patch b/external/meta-updater/recipes-sota/asn1c/files/skeletons_dir_fix.patch
new file mode 100644
index 00000000..f1caa2fd
--- /dev/null
+++ b/external/meta-updater/recipes-sota/asn1c/files/skeletons_dir_fix.patch
@@ -0,0 +1,44 @@
+From 1a1c2c94f700cf0f4dc5dba863950b16477fdc6d Mon Sep 17 00:00:00 2001
+From: Laurent Bonnans <laurent.bonnans@here.com>
+Date: Thu, 25 Jan 2018 09:49:41 +0100
+Subject: [PATCH] Patch the skeletons directory detection
+
+Detect `share/asn1c` from `bin/` if it exists
+---
+ asn1c/asn1c.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/asn1c/asn1c.c b/asn1c/asn1c.c
+index eb1eff7c..dd9fc832 100644
+--- a/asn1c/asn1c.c
++++ b/asn1c/asn1c.c
+@@ -226,22 +226,21 @@ main(int ac, char **av) {
+ if(skeletons_dir == NULL) {
+ struct stat sb;
+ skeletons_dir = DATADIR;
+- if((av[-optind][0] == '.' || av[-optind][1] == '/')
+- && stat(skeletons_dir, &sb)) {
++ if(stat(skeletons_dir, &sb)) {
+ /*
+ * The default skeletons directory does not exist,
+ * compute it from my file name:
+- * ./asn1c/asn1c -> ./skeletons
++ * ./asn1c/asn1c -> ./share/asn1c
+ */
+ char *p;
+ size_t len;
+
+ p = a1c_dirname(av[-optind]);
+
+- len = strlen(p) + sizeof("/../skeletons");
++ len = strlen(p) + sizeof("/../share/asn1c");
+ skeletons_dir = malloc(len);
+ assert(skeletons_dir);
+- snprintf(skeletons_dir, len, "%s/../skeletons", p);
++ snprintf(skeletons_dir, len, "%s/../share/asn1c", p);
+ if(stat(skeletons_dir, &sb)) {
+ fprintf(stderr,
+ "WARNING: skeletons are neither in "
+--
+2.15.1
+
diff --git a/external/meta-updater/recipes-sota/config/aktualizr-auto-reboot.bb b/external/meta-updater/recipes-sota/config/aktualizr-auto-reboot.bb
new file mode 100644
index 00000000..f360d9e1
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/aktualizr-auto-reboot.bb
@@ -0,0 +1,23 @@
+SUMMARY = "Enable auto reboot to apply updates"
+DESCRIPTION = "Configures aktualizr to automatically reboot after new updates are installed in order to apply the updates immediately"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+SRC_URI = " \
+ file://35-enable-auto-reboot.toml \
+ "
+
+do_install_append () {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${WORKDIR}/35-enable-auto-reboot.toml ${D}${libdir}/sota/conf.d/35-enable-auto-reboot.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d/35-enable-auto-reboot.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-sota/config/aktualizr-disable-send-ip.bb b/external/meta-updater/recipes-sota/config/aktualizr-disable-send-ip.bb
new file mode 100644
index 00000000..07c12ca2
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/aktualizr-disable-send-ip.bb
@@ -0,0 +1,24 @@
+SUMMARY = "Disable IP reporting in Aktualizr"
+DESCRIPTION = "Configures aktualizr to disable IP reporting to the server"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+SRC_URI = " \
+ file://30-disable-send-ip.toml \
+ "
+
+do_install_append () {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${WORKDIR}/30-disable-send-ip.toml ${D}${libdir}/sota/conf.d/30-disable-send-ip.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d/30-disable-send-ip.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
+
diff --git a/external/meta-updater/recipes-sota/config/aktualizr-log-debug.bb b/external/meta-updater/recipes-sota/config/aktualizr-log-debug.bb
new file mode 100644
index 00000000..0c03786d
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/aktualizr-log-debug.bb
@@ -0,0 +1,24 @@
+SUMMARY = "Set debug logging in Aktualizr"
+DESCRIPTION = "Configures aktualizr to log at a debugging level"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+SRC_URI = " \
+ file://05-log-debug.toml \
+ "
+
+do_install_append () {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${WORKDIR}/05-log-debug.toml ${D}${libdir}/sota/conf.d/05-log-debug.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d/05-log-debug.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
+
diff --git a/external/meta-updater/recipes-sota/config/aktualizr-polling-interval.bb b/external/meta-updater/recipes-sota/config/aktualizr-polling-interval.bb
new file mode 100644
index 00000000..53c008a0
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/aktualizr-polling-interval.bb
@@ -0,0 +1,29 @@
+SUMMARY = "Set polling interval in Aktualizr"
+DESCRIPTION = "Configures aktualizr to poll at a custom frequency (suitable for testing or other purposes)"
+HOMEPAGE = "https://github.com/advancedtelematic/aktualizr"
+SECTION = "base"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+SRC_URI = " \
+ file://60-polling-interval.toml \
+ "
+
+SOTA_POLLING_SEC ?= "30"
+
+do_install_append () {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${WORKDIR}/60-polling-interval.toml ${D}${libdir}/sota/conf.d/60-polling-interval.toml
+
+ sed -i -e 's|@POLLING_SEC@|${SOTA_POLLING_SEC}|g' \
+ ${D}${libdir}/sota/conf.d/60-polling-interval.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d/60-polling-interval.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
+
diff --git a/external/meta-updater/recipes-sota/config/files/05-log-debug.toml b/external/meta-updater/recipes-sota/config/files/05-log-debug.toml
new file mode 100644
index 00000000..100a1468
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/files/05-log-debug.toml
@@ -0,0 +1,2 @@
+[logger]
+loglevel = 0
diff --git a/external/meta-updater/recipes-sota/config/files/30-disable-send-ip.toml b/external/meta-updater/recipes-sota/config/files/30-disable-send-ip.toml
new file mode 100644
index 00000000..5cd5108d
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/files/30-disable-send-ip.toml
@@ -0,0 +1,2 @@
+[telemetry]
+report_network = false
diff --git a/external/meta-updater/recipes-sota/config/files/35-enable-auto-reboot.toml b/external/meta-updater/recipes-sota/config/files/35-enable-auto-reboot.toml
new file mode 100644
index 00000000..f83810e7
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/files/35-enable-auto-reboot.toml
@@ -0,0 +1,2 @@
+[uptane]
+force_install_completion = true
diff --git a/external/meta-updater/recipes-sota/config/files/60-polling-interval.toml b/external/meta-updater/recipes-sota/config/files/60-polling-interval.toml
new file mode 100644
index 00000000..7d25d057
--- /dev/null
+++ b/external/meta-updater/recipes-sota/config/files/60-polling-interval.toml
@@ -0,0 +1,2 @@
+[uptane]
+polling_sec = @POLLING_SEC@
diff --git a/external/meta-updater/recipes-sota/fit-conf/fit-conf.bb b/external/meta-updater/recipes-sota/fit-conf/fit-conf.bb
new file mode 100644
index 00000000..f24a94ac
--- /dev/null
+++ b/external/meta-updater/recipes-sota/fit-conf/fit-conf.bb
@@ -0,0 +1,24 @@
+SUMMARY = "FIT image configuration for u-boot to use"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
+
+PACKAGE_ARCH = "${MACHINE_ARCH}"
+
+do_install() {
+ mkdir -p ${D}${libdir}
+ echo -n "fit_conf=" >${D}${libdir}/fit_conf
+
+ if [ -n ${SOTA_MAIN_DTB} ]; then
+ echo -n "#conf@${SOTA_MAIN_DTB}" >> ${D}${libdir}/fit_conf
+ fi
+
+ for ovrl in ${SOTA_DT_OVERLAYS}; do
+ echo -n "#conf@overlays_${ovrl}" >> ${D}${libdir}/fit_conf
+ done
+
+ for conf_frag in ${SOTA_EXTRA_CONF_FRAGS}; do
+ echo -n "#${conf_frag}" >> ${D}${libdir}/fit_conf
+ done
+}
+
+FILES_${PN} += "${libdir}/fit_conf"
diff --git a/external/meta-updater/recipes-sota/ostree-initrd/files/init.sh b/external/meta-updater/recipes-sota/ostree-initrd/files/init.sh
new file mode 100644
index 00000000..4818a071
--- /dev/null
+++ b/external/meta-updater/recipes-sota/ostree-initrd/files/init.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+set -eu
+
+# -------------------------------------------
+
+log_info() { echo "$0[$$]: $*" >&2; }
+log_error() { echo "$0[$$]: ERROR $*" >&2; }
+
+do_mount_fs() {
+ log_info "mounting FS: $*"
+ [[ -e /proc/filesystems ]] && { grep -q "$1" /proc/filesystems || { log_error "Unknown filesystem"; return 1; } }
+ [[ -d "$2" ]] || mkdir -p "$2"
+ [[ -e /proc/mounts ]] && { grep -q -e "^$1 $2 $1" /proc/mounts && { log_info "$2 ($1) already mounted"; return 0; } }
+ mount -t "$1" "$1" "$2"
+}
+
+bail_out() {
+ log_error "$@"
+ log_info "Rebooting..."
+ #exec reboot -f
+ exec sh
+}
+
+get_ostree_sysroot() {
+ for opt in $(cat /proc/cmdline); do
+ arg=$(echo "$opt" | cut -d'=' -f1)
+ if [ "$arg" == "ostree_root" ]; then
+ echo "$opt" | cut -d'=' -f2-
+ return
+ fi
+ done
+ echo "LABEL=otaroot"
+}
+
+export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/lib/ostree
+
+log_info "Starting OSTree initrd script"
+
+do_mount_fs proc /proc
+do_mount_fs sysfs /sys
+do_mount_fs devtmpfs /dev
+do_mount_fs devpts /dev/pts
+do_mount_fs tmpfs /dev/shm
+do_mount_fs tmpfs /run
+
+# check if smack is active (and if so, mount smackfs)
+grep -q smackfs /proc/filesystems && {
+ do_mount_fs smackfs /sys/fs/smackfs
+
+ # adjust current label and network label
+ echo System >/proc/self/attr/current
+ echo System >/sys/fs/smackfs/ambient
+}
+
+mkdir -p /sysroot
+ostree_sysroot=$(get_ostree_sysroot)
+
+mount "$ostree_sysroot" /sysroot || {
+ # The SD card in the R-Car M3 takes a bit of time to come up
+ # Retry the mount if it fails the first time
+ log_info "Mounting $ostree_sysroot failed, waiting 5s for the device to be available..."
+ sleep 5
+ mount "$ostree_sysroot" /sysroot || bail_out "Unable to mount $ostree_sysroot as physical sysroot"
+}
+
+ostree-prepare-root /sysroot
+
+log_info "Switching to rootfs"
+exec switch_root /sysroot /sbin/init
+
+bail_out "Failed to switch_root to $ostree_sysroot"
diff --git a/external/meta-updater/recipes-sota/ostree-initrd/ostree-initrd.bb b/external/meta-updater/recipes-sota/ostree-initrd/ostree-initrd.bb
new file mode 100644
index 00000000..26525c89
--- /dev/null
+++ b/external/meta-updater/recipes-sota/ostree-initrd/ostree-initrd.bb
@@ -0,0 +1,20 @@
+SUMMARY = "Initramfs for booting into libostree managed system"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
+
+SRC_URI = "file://init.sh"
+
+S = "${WORKDIR}"
+
+PV = "4"
+
+do_install() {
+ install -dm 0755 ${D}/etc
+ touch ${D}/etc/initrd-release
+ install -dm 0755 ${D}/dev
+ install -m 0755 ${WORKDIR}/init.sh ${D}/init
+}
+
+inherit allarch
+
+FILES_${PN} += " /dev /etc/initrd-release /init "
diff --git a/external/meta-updater/recipes-sota/ostree/ostree_git.bb b/external/meta-updater/recipes-sota/ostree/ostree_git.bb
new file mode 100644
index 00000000..93ae6e74
--- /dev/null
+++ b/external/meta-updater/recipes-sota/ostree/ostree_git.bb
@@ -0,0 +1,69 @@
+SUMMARY = "Tool for managing bootable, immutable, versioned filesystem trees"
+HOMEPAGE = "https://ostree.readthedocs.io/en/latest/"
+LICENSE = "LGPLv2+"
+LIC_FILES_CHKSUM = "file://COPYING;md5=5f30f0716dfdd0d91eb439ebec522ec2"
+
+inherit autotools pkgconfig systemd bash-completion gobject-introspection
+
+SRC_URI = "gitsm://github.com/ostreedev/ostree.git;branch=master"
+
+SRCREV = "f3eba6bcec39c163eb831c02c148ffa483292906"
+
+PV = "v2018.9"
+
+S = "${WORKDIR}/git"
+
+BBCLASSEXTEND = "native"
+
+DEPENDS += "attr bison-native libarchive libcap glib-2.0 gpgme fuse e2fsprogs curl xz"
+DEPENDS += "${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)}"
+RDEPENDS_${PN}-dracut = "bash"
+
+CFLAGS_append = " -Wno-error=missing-prototypes"
+EXTRA_OECONF = "--disable-gtk-doc --disable-man --with-smack --with-builtin-grub2-mkconfig --with-curl --without-soup"
+EXTRA_OECONF_append_class-native = " --enable-wrpseudo-compat"
+
+PACKAGECONFIG ??= "${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)}"
+PACKAGECONFIG[systemd] = "--with-systemdsystemunitdir=${systemd_unitdir}/system/ --with-dracut"
+
+# Path to ${prefix}/lib/ostree/ostree-grub-generator is hardcoded on the
+# do_configure stage so we do depend on it
+SYSROOT_DIR = "${STAGING_DIR_TARGET}"
+SYSROOT_DIR_class-native = "${STAGING_DIR_NATIVE}"
+do_configure[vardeps] += "SYSROOT_DIR"
+
+SYSTEMD_SERVICE_${PN} = "ostree-prepare-root.service ostree-remount.service ostree-finalize-staged.service"
+
+export BUILD_SYS
+export HOST_SYS
+export STAGING_INCDIR
+export STAGING_LIBDIR
+
+do_configure_prepend() {
+ unset docdir
+ NOCONFIGURE=1 "${S}/autogen.sh"
+}
+
+do_install_append_class-native() {
+ create_wrapper ${D}${bindir}/ostree OSTREE_GRUB2_EXEC="${STAGING_LIBDIR_NATIVE}/ostree/ostree-grub-generator"
+}
+
+PACKAGES += " \
+ ${PN}-switchroot \
+ ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'ostree-dracut', '', d)} \
+"
+
+FILES_${PN} = "${bindir} \
+ ${sysconfdir}/ostree \
+ ${datadir}/ostree \
+ ${libdir}/*.so.* \
+ ${libdir}/ostree/ostree-grub-generator \
+ ${libdir}/ostree/ostree-remount \
+ ${libdir}/girepository-1.0/* \
+ ${@bb.utils.contains('DISTRO_FEATURES','systemd','${libdir}/tmpfiles.d', '', d)} \
+ ${@bb.utils.contains('DISTRO_FEATURES','systemd','${systemd_unitdir}/system/*.path', '', d)} \
+ ${@bb.utils.contains('DISTRO_FEATURES','systemd','${systemd_unitdir}/system-generators', '', d)} \
+"
+FILES_${PN}-dev += " ${datadir}/gir-1.0"
+FILES_${PN}-dracut = "${sysconfdir}/dracut.conf.d ${libdir}/dracut"
+FILES_${PN}-switchroot = "${libdir}/ostree/ostree-prepare-root"
diff --git a/external/meta-updater/recipes-support/fuse/fuse_%.bbappend b/external/meta-updater/recipes-support/fuse/fuse_%.bbappend
new file mode 100644
index 00000000..85bdf502
--- /dev/null
+++ b/external/meta-updater/recipes-support/fuse/fuse_%.bbappend
@@ -0,0 +1,3 @@
+BBCLASSEXTEND_append_sota = " native"
+
+PACKAGES_append_class-native_sota = "${@bb.utils.contains('DISTRO_FEATURES', 'sota', ' fuse-utils-dbg-native fuse-utils-native libulockmgr-native libulockmgr-dev-native libulockmgr-dbg-native', ' ', d)}"
diff --git a/external/meta-updater/recipes-support/gpgme/gpgme_%.bbappend b/external/meta-updater/recipes-support/gpgme/gpgme_%.bbappend
new file mode 100644
index 00000000..fccb9499
--- /dev/null
+++ b/external/meta-updater/recipes-support/gpgme/gpgme_%.bbappend
@@ -0,0 +1 @@
+BBCLASSEXTEND_append_sota = " native"
diff --git a/external/meta-updater/recipes-support/libp11/libp11_git.bb b/external/meta-updater/recipes-support/libp11/libp11_git.bb
new file mode 100644
index 00000000..bedcdc82
--- /dev/null
+++ b/external/meta-updater/recipes-support/libp11/libp11_git.bb
@@ -0,0 +1,40 @@
+SUMMARY = "Library for using PKCS"
+DESCRIPTION = "\
+Libp11 is a library implementing a small layer on top of PKCS \
+make using PKCS"
+HOMEPAGE = "http://www.opensc-project.org/libp11"
+SECTION = "Development/Libraries"
+LICENSE = "LGPLv2+"
+LIC_FILES_CHKSUM = "file://COPYING;md5=fad9b3332be894bab9bc501572864b29"
+DEPENDS = "libtool openssl"
+RDEPENDS_${PN} += " opensc"
+
+SRC_URI = "git://github.com/OpenSC/libp11.git"
+SRCREV = "57ca68ff67efa08e3be1f26dec6d23bf5bb977f2"
+
+PV = "0.4.9+git${SRCPV}"
+
+S = "${WORKDIR}/git"
+
+inherit autotools pkgconfig
+
+# Currently, Makefile dependencies are incorrectly defined which causes build errors
+# if the number of jobs is high
+# See https://github.com/OpenSC/libp11/issues/94
+PARALLEL_MAKE = ""
+EXTRA_OECONF = "--disable-static"
+
+do_install_append () {
+ rm -rf ${D}${libdir}/*.la
+ rm -rf ${D}${docdir}/${BPN}
+}
+
+FILES_${PN} = "${libdir}/engines*/pkcs11.so \
+ ${libdir}/engines*/libpkcs11${SOLIBS} \
+ ${libdir}/libp11${SOLIBS}"
+
+FILES_${PN}-dev = " \
+ ${libdir}/engines*/libpkcs11${SOLIBSDEV} \
+ ${libdir}/libp11${SOLIBSDEV} \
+ ${libdir}/pkgconfig/libp11.pc \
+ /usr/include"
diff --git a/external/meta-updater/recipes-support/libssh2/libssh2_%.bbappend b/external/meta-updater/recipes-support/libssh2/libssh2_%.bbappend
new file mode 100644
index 00000000..bebaf84c
--- /dev/null
+++ b/external/meta-updater/recipes-support/libssh2/libssh2_%.bbappend
@@ -0,0 +1,2 @@
+# meta-oe/recipes-support/libssh2
+BBCLASSEXTEND_append_sota = " native"
diff --git a/external/meta-updater/recipes-support/lshw/files/cross-compile.patch b/external/meta-updater/recipes-support/lshw/files/cross-compile.patch
new file mode 100644
index 00000000..221b7e53
--- /dev/null
+++ b/external/meta-updater/recipes-support/lshw/files/cross-compile.patch
@@ -0,0 +1,39 @@
+---
+ src/Makefile | 2 +-
+ src/core/Makefile | 2 +-
+ src/gui/Makefile | 4 ++--
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/src/Makefile
++++ b/src/Makefile
+@@ -18,7 +18,7 @@ export MANDIR
+ export DATADIR
+ export SQLITE
+
+-CXX?=c++
++CXX?=$(CROSS_COMPILE)c++
+ INCLUDES=-I./core/
+ DEFINES=-DPREFIX=\"$(PREFIX)\" -DSBINDIR=\"$(SBINDIR)\" -DMANDIR=\"$(MANDIR)\" -DDATADIR=\"$(DATADIR)\"
+ CXXFLAGS=-g -Wall -g $(INCLUDES) $(DEFINES) $(RPM_OPT_FLAGS)
+--- a/src/core/Makefile
++++ b/src/core/Makefile
+@@ -1,6 +1,6 @@
+ PACKAGENAME?=lshw
+
+-CXX=c++
++CXX?=$(CROSS_COMPILE)c++
+ INCLUDES=
+ DEFINES=-DPREFIX=\"$(PREFIX)\" -DSBINDIR=\"$(SBINDIR)\" -DMANDIR=\"$(MANDIR)\" -DDATADIR=\"$(DATADIR)\"
+ CXXFLAGS?=-g -Wall $(INCLUDES) $(DEFINES) $(RPM_OPT_FLAGS)
+--- a/src/gui/Makefile
++++ b/src/gui/Makefile
+@@ -1,7 +1,7 @@
+ PACKAGENAME?=lshw
+
+-CXX?=c++
+-CC?=cc
++CXX?=$(CROSS_COMPILE)c++
++CC?=$(CROSS_COMPILE)cc
+ STRIP?=strip
+ OBJCOPY?=objcopy
+
diff --git a/external/meta-updater/recipes-support/lshw/files/ldflags.patch b/external/meta-updater/recipes-support/lshw/files/ldflags.patch
new file mode 100644
index 00000000..d95699d2
--- /dev/null
+++ b/external/meta-updater/recipes-support/lshw/files/ldflags.patch
@@ -0,0 +1,42 @@
+diff -Naur /home/anton/lshw-old/src/Makefile lshw-B.02.17/src/Makefile
+--- /home/anton/lshw-old/src/Makefile 2017-02-07 16:21:52.554738182 +0100
++++ lshw-B.02.17/src/Makefile 2017-02-07 16:22:45.578588072 +0100
+@@ -25,9 +25,9 @@
+ ifeq ($(SQLITE), 1)
+ CXXFLAGS+= -DSQLITE $(shell pkg-config --cflags sqlite3)
+ endif
+-LDFLAGS=-L./core/ -g
++LDEXTRAS=-L./core/ -g
+ ifneq ($(shell $(LD) --help 2| grep -- --as-needed), )
+- LDFLAGS+= -Wl,--as-needed
++ LDEXTRAS+= -Wl,--as-needed
+ endif
+ LDSTATIC=-static
+ LIBS=-llshw -lresolv
+@@ -37,7 +37,7 @@
+
+ export CXXFLAGS
+ export LIBS
+-export LDFLAGS
++export LDEXTRAS
+
+ DATAFILES = pci.ids usb.ids oui.txt manuf.txt
+
+@@ -51,7 +51,7 @@
+ +make -C core all
+
+ $(PACKAGENAME): core $(PACKAGENAME).o
+- $(CXX) $(LDFLAGS) -o $@ $(PACKAGENAME).o $(LIBS)
++ $(CXX) $(LDFLAGS) ${LDEXTRAS} -o $@ $(PACKAGENAME).o $(LIBS)
+
+ .PHONY: po
+ po:
+@@ -69,7 +69,7 @@
+ static: $(PACKAGENAME)-static
+
+ $(PACKAGENAME)-static: core core/lib$(PACKAGENAME).a $(PACKAGENAME).o
+- $(CXX) $(LDSTATIC) $(LDFLAGS) -o $@ $(PACKAGENAME).o $(LIBS)
++ $(CXX) $(LDSTATIC) $(LDFLAGS) ${LDEXTRAS} -o $@ $(PACKAGENAME).o $(LIBS)
+ $(STRIP) $@
+
+ .PHONY: compressed
diff --git a/external/meta-updater/recipes-support/lshw/lshw_02.17.bb b/external/meta-updater/recipes-support/lshw/lshw_02.17.bb
new file mode 100644
index 00000000..1ecc4c10
--- /dev/null
+++ b/external/meta-updater/recipes-support/lshw/lshw_02.17.bb
@@ -0,0 +1,36 @@
+# From meta-linaro
+# http://git.linaro.org/openembedded/meta-linaro.git
+
+DESCRIPTION = "A small tool to provide detailed information on the hardware \
+configuration of the machine. It can report exact memory configuration, \
+firmware version, mainboard configuration, CPU version and speed, cache \
+configuration, bus speed, etc. on DMI-capable or EFI systems."
+SUMMARY = "Hardware lister"
+HOMEPAGE = "http://ezix.org/project/wiki/HardwareLiSter"
+SECTION = "console/tools"
+LICENSE = "GPLv2+"
+LIC_FILES_CHKSUM = "file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263"
+DEPENDS = "pciutils \
+ usbutils"
+COMPATIBLE_HOST = "(i.86|x86_64|arm|aarch64).*-linux"
+
+SRC_URI="http://ezix.org/software/files/lshw-B.${PV}.tar.gz \
+ file://cross-compile.patch \
+ file://ldflags.patch \
+ "
+
+SRC_URI[md5sum] = "a5feb796cb302850eaf5b4530888e3ed"
+SRC_URI[sha256sum] = "eb9cc053fa0f1e78685cb695596e73931bfb55d2377e3bc3b8b94aff4c5a489c"
+
+S="${WORKDIR}/lshw-B.${PV}"
+
+do_compile() {
+ # build core only - don't ship gui
+ oe_runmake -C src core
+}
+
+do_install() {
+ oe_runmake install DESTDIR=${D}
+ # data files provided by dependencies
+ rm -rf ${D}/usr/share/lshw
+}
diff --git a/external/meta-updater/recipes-support/slcand-start/files/slcand@.service b/external/meta-updater/recipes-support/slcand-start/files/slcand@.service
new file mode 100644
index 00000000..c5395680
--- /dev/null
+++ b/external/meta-updater/recipes-support/slcand-start/files/slcand@.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Serial CAN daemon (can-utils)
+
+[Service]
+Type=forking
+ExecStart=/usr/bin/slcand -o -c -s4 %I can0
+ExecStartPost=/bin/sh -c '/bin/sleep 3; /sbin/ip link set can0 up'
+
diff --git a/external/meta-updater/recipes-support/slcand-start/slcand-start.bb b/external/meta-updater/recipes-support/slcand-start/slcand-start.bb
new file mode 100644
index 00000000..dfefaeab
--- /dev/null
+++ b/external/meta-updater/recipes-support/slcand-start/slcand-start.bb
@@ -0,0 +1,21 @@
+SUMMARY = "Mock smartcard for aktualizr"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COREBASE}/LICENSE;md5=4d92cd373abda3937c2bc47fbc49d690 \
+ file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
+
+
+inherit systemd
+
+RDEPENDS_${PN} = "can-utils"
+
+SRC_URI = "file://slcand@.service"
+
+SYSTEMD_SERVICE_${PN} = "slcand@.service"
+
+do_install() {
+ install -d ${D}${systemd_unitdir}/system
+ install -m 0644 ${WORKDIR}/slcand@.service ${D}${systemd_unitdir}/system/slcand@.service
+}
+
+FILES_${PN} = "${systemd_unitdir}/system/createtoken.service"
+
diff --git a/external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.service b/external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.service
new file mode 100644
index 00000000..23317b90
--- /dev/null
+++ b/external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Create a mock smartcard for testing
+Before=aktualizr.service
+RequiredBy=aktualizr.service
+
+[Service]
+RestartSec=10
+Restart=on-failure
+ExecStart=/usr/bin/createtoken.sh
+
+[Install]
+WantedBy=aktualizr.service
diff --git a/external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.sh b/external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.sh
new file mode 100644
index 00000000..fa4569d9
--- /dev/null
+++ b/external/meta-updater/recipes-support/softhsm-testtoken/files/createtoken.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+if pkcs11-tool --module=/usr/lib/softhsm/libsofthsm2.so -O; then
+ # The token has already been initialized, exit
+ exit 0
+fi
+
+if ! ls /var/sota/import/pkey.pem /var/sota/import/client.pem; then
+ # Key/certificate pair is not present, repeat
+ exit 1
+fi
+
+mkdir -p /var/lib/softhsm/tokens
+softhsm2-util --init-token --slot 0 --label "Virtual token" --pin 1234 --so-pin 1234
+
+openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in /var/sota/import/pkey.pem -out /var/sota/import/pkey.p8
+softhsm2-util --import /var/sota/import/pkey.p8 --label "pkey" --id 02 --token 'Virtual token' --pin 1234
+openssl x509 -outform der -in /var/sota/import/client.pem -out /var/sota/import/client.der
+pkcs11-tool --module=/usr/lib/softhsm/libsofthsm2.so --id 1 --write-object /var/sota/import/client.der --type cert --login --pin 1234
+
+# Import UPTANE keypair if it exists
+if [ -f /var/sota/import/ecukey.pem ]; then
+ openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in /var/sota/import/ecukey.pem -out /var/sota/import/ecukey.p8
+ softhsm2-util --import /var/sota/import/ecukey.p8 --label "uptanekey" --id 03 --token 'Virtual token' --pin 1234
+fi
+
+exit 0
diff --git a/external/meta-updater/recipes-support/softhsm-testtoken/softhsm-testtoken.bb b/external/meta-updater/recipes-support/softhsm-testtoken/softhsm-testtoken.bb
new file mode 100644
index 00000000..58b521c3
--- /dev/null
+++ b/external/meta-updater/recipes-support/softhsm-testtoken/softhsm-testtoken.bb
@@ -0,0 +1,25 @@
+SUMMARY = "Mock smartcard for aktualizr"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+inherit systemd
+
+RDEPENDS_${PN} = "softhsm libp11 openssl-bin"
+DEPENDS_append = "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', ' systemd', '', d)}"
+
+
+SRC_URI = "file://createtoken.service \
+ file://createtoken.sh"
+
+SYSTEMD_SERVICE_${PN} = "createtoken.service"
+
+do_install() {
+ install -d ${D}${systemd_unitdir}/system
+ install -m 0644 ${WORKDIR}/createtoken.service ${D}${systemd_unitdir}/system/createtoken.service
+ install -d ${D}${bindir}
+ install -m 0744 ${WORKDIR}/createtoken.sh ${D}${bindir}/createtoken.sh
+}
+
+FILES_${PN} = "${bindir}/createtoken.sh \
+ ${systemd_unitdir}/system/createtoken.service"
+
diff --git a/external/meta-updater/recipes-support/softhsm/softhsm_git.bb b/external/meta-updater/recipes-support/softhsm/softhsm_git.bb
new file mode 100644
index 00000000..4dcfe7db
--- /dev/null
+++ b/external/meta-updater/recipes-support/softhsm/softhsm_git.bb
@@ -0,0 +1,26 @@
+SUMMARY = "HSM emulator"
+HOMEPAGE = "https://www.opendnssec.org/softhsm/"
+LICENSE = "BSD-2-Clause & ISC"
+LIC_FILES_CHKSUM = "file://LICENSE;md5=ef3f77a3507c3d91e75b9f2bdaee4210"
+
+DEPENDS = "openssl"
+
+SRC_URI = "git://github.com/opendnssec/SoftHSMv2.git;branch=master"
+SRCREV = "369df0383d101bc8952692c2a368ac8bc887d1b4"
+
+PV = "2.5.0"
+
+S = "${WORKDIR}/git"
+
+inherit autotools pkgconfig
+
+# EdDSA requires OpenSSL >= 1.1.1
+EXTRA_OECONF = "--enable-eddsa --disable-gost"
+
+do_configure_prepend() {
+ (
+ cd ${S}
+ unset docdir
+ sh ./autogen.sh
+ )
+}
diff --git a/external/meta-updater/recipes-support/systemd-journald-persistent/files/10-persistent-journal.conf b/external/meta-updater/recipes-support/systemd-journald-persistent/files/10-persistent-journal.conf
new file mode 100644
index 00000000..a56527c8
--- /dev/null
+++ b/external/meta-updater/recipes-support/systemd-journald-persistent/files/10-persistent-journal.conf
@@ -0,0 +1,3 @@
+[Journal]
+Storage=persistent
+SystemMaxUse=64M
diff --git a/external/meta-updater/recipes-support/systemd-journald-persistent/systemd-journald-persistent.bb b/external/meta-updater/recipes-support/systemd-journald-persistent/systemd-journald-persistent.bb
new file mode 100644
index 00000000..1715fe11
--- /dev/null
+++ b/external/meta-updater/recipes-support/systemd-journald-persistent/systemd-journald-persistent.bb
@@ -0,0 +1,20 @@
+SUMMARY = "Configuration for systemd-journald"
+DESCRIPTION = "Provides configuration for systemd-journald, so that logs are \
+stored on persistent storage"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+PACKAGE_ARCH = "${MACHINE_ARCH}"
+
+SRC_URI_append = " file://10-persistent-journal.conf"
+PR = "r1"
+
+S = "${WORKDIR}"
+
+FILES_${PN} = "${systemd_unitdir}/journald.conf.d/*"
+
+do_install() {
+ install -d ${D}/${systemd_unitdir}/journald.conf.d
+ install -m 0644 ${WORKDIR}/10-persistent-journal.conf ${D}/${systemd_unitdir}/journald.conf.d
+}
+
diff --git a/external/meta-updater/recipes-test/big-update/big-update_1.0.bb b/external/meta-updater/recipes-test/big-update/big-update_1.0.bb
new file mode 100644
index 00000000..3b1d6523
--- /dev/null
+++ b/external/meta-updater/recipes-test/big-update/big-update_1.0.bb
@@ -0,0 +1,13 @@
+DESCRIPTION = "Example Package with 10MB of random, seeded content"
+LICENSE = "MPL-2.0"
+
+SRC_URI = "file://rand_file.py"
+
+FILES_${PN} = "/usr/lib/big-update"
+
+DEPENDS = "coreutils-native"
+
+do_install() {
+ install -d ${D}/usr/lib/big-update
+ python ${S}/../rand_file.py ${D}/usr/lib/big-update/a-big-file $(numfmt --from=iec 10M)
+}
diff --git a/external/meta-updater/recipes-test/big-update/big-update_2.0.bb b/external/meta-updater/recipes-test/big-update/big-update_2.0.bb
new file mode 100644
index 00000000..7cb6e949
--- /dev/null
+++ b/external/meta-updater/recipes-test/big-update/big-update_2.0.bb
@@ -0,0 +1,13 @@
+DESCRIPTION = "Example Package with 12MB of random, seeded content"
+LICENSE = "MPL-2.0"
+
+SRC_URI = "file://rand_file.py"
+
+FILES_${PN} = "/usr/lib/big-update"
+
+DEPENDS = "coreutils-native"
+
+do_install() {
+ install -d ${D}/usr/lib/big-update
+ python ${S}/../rand_file.py ${D}/usr/lib/big-update/a-big-file $(numfmt --from=iec 12M)
+}
diff --git a/external/meta-updater/recipes-test/big-update/files/rand_file.py b/external/meta-updater/recipes-test/big-update/files/rand_file.py
new file mode 100644
index 00000000..0f4f16e4
--- /dev/null
+++ b/external/meta-updater/recipes-test/big-update/files/rand_file.py
@@ -0,0 +1,16 @@
+import sys
+from random import seed, randint
+
+def main():
+ n = int(sys.argv[2])
+ ba = bytearray(n)
+
+ seed(42)
+ for i in range(0, n):
+ ba[i] = randint(0, 255)
+
+ with open(sys.argv[1], 'wb') as f:
+ f.write(bytes(ba))
+
+if __name__ == "__main__":
+ main()
diff --git a/external/meta-updater/recipes-test/demo-config/files/30-fake-pacman.toml b/external/meta-updater/recipes-test/demo-config/files/30-fake-pacman.toml
new file mode 100644
index 00000000..3fb5cf2c
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/files/30-fake-pacman.toml
@@ -0,0 +1,2 @@
+[pacman]
+type = "fake"
diff --git a/external/meta-updater/recipes-test/demo-config/files/30-secondary-config.toml b/external/meta-updater/recipes-test/demo-config/files/30-secondary-config.toml
new file mode 100644
index 00000000..77142408
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/files/30-secondary-config.toml
@@ -0,0 +1,2 @@
+[uptane]
+secondary_config_file = "@CFG_FILEPATH@"
diff --git a/external/meta-updater/recipes-test/demo-config/files/35-network-config.toml b/external/meta-updater/recipes-test/demo-config/files/35-network-config.toml
new file mode 100644
index 00000000..db7a1bb0
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/files/35-network-config.toml
@@ -0,0 +1,4 @@
+[network]
+port = @PORT@
+primary_ip = @PRIMARY_IP@
+primary_port = @PRIMARY_PORT@
diff --git a/external/meta-updater/recipes-test/demo-config/files/45-id-config.toml b/external/meta-updater/recipes-test/demo-config/files/45-id-config.toml
new file mode 100644
index 00000000..6cbd77f9
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/files/45-id-config.toml
@@ -0,0 +1,3 @@
+[uptane]
+ecu_serial = @SERIAL@
+ecu_hardware_id = @HWID@
diff --git a/external/meta-updater/recipes-test/demo-config/files/ip_secondary_config.json b/external/meta-updater/recipes-test/demo-config/files/ip_secondary_config.json
new file mode 100644
index 00000000..690cf2eb
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/files/ip_secondary_config.json
@@ -0,0 +1,7 @@
+{
+ "IP": {
+ "secondaries_wait_port": @PORT@,
+ "secondaries_wait_timeout": @TIMEOUT@,
+ "secondaries": @ADDR_ARRAY@
+ }
+}
diff --git a/external/meta-updater/recipes-test/demo-config/primary-config.bb b/external/meta-updater/recipes-test/demo-config/primary-config.bb
new file mode 100644
index 00000000..27cb553e
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/primary-config.bb
@@ -0,0 +1,68 @@
+DESCRIPTION = "Sample configuration for an Uptane Primary to support IP/Posix Secondary"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+require shared-conf.inc
+
+PRIMARY_SECONDARIES ?= "${SECONDARY_IP}:${SECONDARY_PORT}"
+
+SRC_URI = "\
+ file://30-secondary-config.toml \
+ file://ip_secondary_config.json \
+ "
+
+def get_secondary_addrs(d):
+ import json
+
+ secondaries = d.getVar('PRIMARY_SECONDARIES')
+ sec_array = []
+ for secondary in secondaries.split():
+ sec_array.append({"addr": secondary})
+
+ return json.dumps(sec_array)
+
+do_install () {
+
+ if [ ! -n "${SOTA_SECONDARY_CONFIG}" ]; then
+ bbwarn "SOTA_SECONDARY_CONFIG hasn't been specified in the local config, generate a default one"
+
+ IP_SECONDARY_CONFIG_FILE=${WORKDIR}/ip_secondary_config.json
+ IP_SECONDARY_ADDRS='${@get_secondary_addrs(d)}'
+ else
+ bbwarn "SOTA_SECONDARY_CONFIG has been specified in the local config: ${SOTA_SECONDARY_CONFIG}"
+
+ IP_SECONDARY_CONFIG_FILE=${SOTA_SECONDARY_CONFIG}
+ fi
+
+ if [ ! -f $IP_SECONDARY_CONFIG_FILE ]; then
+ bbfatal "Secondary config file does not exist: $IP_SECONDARY_CONFIG_FILE"
+ fi
+
+ SECONDARY_CONFIG_DEST_DIR="${D}${sysconfdir}/sota/ecus"
+ SECONDARY_CONFIG_DEST_FILEPATH=$SECONDARY_CONFIG_DEST_DIR/$(basename -- $IP_SECONDARY_CONFIG_FILE)
+ SECONDARY_CONFIG_FILEPATH_ON_IMAGE="${sysconfdir}/sota/ecus/$(basename -- $IP_SECONDARY_CONFIG_FILE)"
+
+ # install the secondary configuration file (json)
+ install -m 0700 -d $SECONDARY_CONFIG_DEST_DIR
+ install -m 0644 $IP_SECONDARY_CONFIG_FILE $SECONDARY_CONFIG_DEST_DIR
+
+ # if SOTA_SECONDARY_CONFIG/secondary config file is not defined in the local conf
+ # then a default template is used and filled with corresponding configuration variable values
+ if [ ! -n "${SOTA_SECONDARY_CONFIG}" ]; then
+ sed -i -e "s|@PORT@|${PRIMARY_PORT}|g" \
+ -e "s|@TIMEOUT@|${PRIMARY_WAIT_TIMEOUT}|g" \
+ -e "s|@ADDR_ARRAY@|$IP_SECONDARY_ADDRS|g" $SECONDARY_CONFIG_DEST_FILEPATH
+ fi
+
+ # install aktualizr config file (toml) that points to the secondary config file, so aktualizr is aware about it
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${WORKDIR}/30-secondary-config.toml ${D}${libdir}/sota/conf.d
+ sed -i "s|@CFG_FILEPATH@|$SECONDARY_CONFIG_FILEPATH_ON_IMAGE|g" ${D}${libdir}/sota/conf.d/30-secondary-config.toml
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d/* \
+ ${sysconfdir}/sota/ecus/* \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-test/demo-config/secondary-config.bb b/external/meta-updater/recipes-test/demo-config/secondary-config.bb
new file mode 100644
index 00000000..9411646b
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/secondary-config.bb
@@ -0,0 +1,41 @@
+DESCRIPTION = "Sample configuration for an Uptane Secondary"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+require shared-conf.inc
+
+SECONDARY_SERIAL_ID ?= ""
+SOTA_HARDWARE_ID ?= "${MACHINE}-sndry"
+SECONDARY_HARDWARE_ID ?= "${SOTA_HARDWARE_ID}"
+
+SRC_URI = "\
+ file://30-fake-pacman.toml \
+ file://35-network-config.toml \
+ file://45-id-config.toml \
+ "
+
+do_install () {
+ install -m 0700 -d ${D}${libdir}/sota/conf.d
+ install -m 0644 ${WORKDIR}/30-fake-pacman.toml ${D}/${libdir}/sota/conf.d/30-fake-pacman.toml
+
+ install -m 0644 ${WORKDIR}/35-network-config.toml ${D}/${libdir}/sota/conf.d/35-network-config.toml
+ sed -i -e 's|@PORT@|${SECONDARY_PORT}|g' \
+ -e 's|@PRIMARY_IP@|${PRIMARY_IP}|g' \
+ -e 's|@PRIMARY_PORT@|${PRIMARY_PORT}|g' \
+ ${D}/${libdir}/sota/conf.d/35-network-config.toml
+
+ install -m 0644 ${WORKDIR}/45-id-config.toml ${D}/${libdir}/sota/conf.d/45-id-config.toml
+ sed -i -e 's|@SERIAL@|${SECONDARY_SERIAL_ID}|g' \
+ -e 's|@HWID@|${SECONDARY_HARDWARE_ID}|g' \
+ ${D}/${libdir}/sota/conf.d/45-id-config.toml
+
+}
+
+FILES_${PN} = " \
+ ${libdir}/sota/conf.d \
+ ${libdir}/sota/conf.d/30-fake-pacman.toml \
+ ${libdir}/sota/conf.d/35-network-config.toml \
+ ${libdir}/sota/conf.d/45-id-config.toml \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-test/demo-config/shared-conf.inc b/external/meta-updater/recipes-test/demo-config/shared-conf.inc
new file mode 100644
index 00000000..c5ab5987
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-config/shared-conf.inc
@@ -0,0 +1,5 @@
+SECONDARY_IP ?= "10.0.3.2"
+SECONDARY_PORT ?= "9050"
+PRIMARY_IP ?= "10.0.3.1"
+PRIMARY_PORT ?= "9040"
+PRIMARY_WAIT_TIMEOUT ?= "240"
diff --git a/external/meta-updater/recipes-test/demo-network-config/files/25-dhcp-server.network b/external/meta-updater/recipes-test/demo-network-config/files/25-dhcp-server.network
new file mode 100644
index 00000000..4766f9ae
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/files/25-dhcp-server.network
@@ -0,0 +1,12 @@
+[Match]
+Name=enp0s4
+
+[Network]
+Description=Private internal network between aktualizr Primary and Secondary nodes
+DHCPServer=yes
+Address=10.0.3.1/24
+IPForward=yes
+IPMasquerade=yes
+
+[DHCPServer]
+PoolOffset=10 \ No newline at end of file
diff --git a/external/meta-updater/recipes-test/demo-network-config/files/26-dhcp-client.network b/external/meta-updater/recipes-test/demo-network-config/files/26-dhcp-client.network
new file mode 100644
index 00000000..319664f8
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/files/26-dhcp-client.network
@@ -0,0 +1,6 @@
+[Match]
+Name=enp0s4
+
+[Network]
+Description=Private internal network between aktualizr Primary and Secondary nodes
+DHCP=yes
diff --git a/external/meta-updater/recipes-test/demo-network-config/files/26-multihomed-client.network b/external/meta-updater/recipes-test/demo-network-config/files/26-multihomed-client.network
new file mode 100644
index 00000000..f1e6cc63
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/files/26-multihomed-client.network
@@ -0,0 +1,9 @@
+[Match]
+Name=@IFNAME@
+
+[Network]
+Description=Multihomed network. DHCP-assigned IP for Primary<->Backend. Statically assigned IP for Primary<->Secondary
+DHCP=yes
+
+[Address]
+Address=@ADDR@
diff --git a/external/meta-updater/recipes-test/demo-network-config/files/26-static-client.network b/external/meta-updater/recipes-test/demo-network-config/files/26-static-client.network
new file mode 100644
index 00000000..19a6b83f
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/files/26-static-client.network
@@ -0,0 +1,7 @@
+[Match]
+Name=@IFNAME@
+
+[Network]
+Description=Private internal network between aktualizr Primary and Secondary nodes
+Address=@ADDR@
+DHCP=no
diff --git a/external/meta-updater/recipes-test/demo-network-config/files/27-dhcp-client-external.network b/external/meta-updater/recipes-test/demo-network-config/files/27-dhcp-client-external.network
new file mode 100644
index 00000000..ba495939
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/files/27-dhcp-client-external.network
@@ -0,0 +1,6 @@
+[Match]
+Name=enp0s3
+
+[Network]
+Description=External network for secondary
+DHCP=yes
diff --git a/external/meta-updater/recipes-test/demo-network-config/network-config.inc b/external/meta-updater/recipes-test/demo-network-config/network-config.inc
new file mode 100644
index 00000000..ed623d46
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/network-config.inc
@@ -0,0 +1,17 @@
+SRC_URI_append = "\
+ file://26-${CONF_TYPE}-client.network \
+ "
+
+SECONDARY_INTERFACE ?= "${@ 'eth0' if d.getVar('MACHINE') == 'raspberrypi3' else 'enp0s5'}"
+
+do_install_append() {
+ bbnote "Network configuration type to be applied: ${CONF_TYPE}"
+ install -d ${D}/usr/lib/systemd/network
+ install -m 0644 ${WORKDIR}/26-${CONF_TYPE}-client.network ${D}/usr/lib/systemd/network/
+ sed -i -e 's|@ADDR@|${IP_ADDR}|g' \
+ -e 's|@IFNAME@|${SECONDARY_INTERFACE}|g' \
+ ${D}/usr/lib/systemd/network/26-${CONF_TYPE}-client.network
+
+}
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-test/demo-network-config/primary-network-config.bb b/external/meta-updater/recipes-test/demo-network-config/primary-network-config.bb
new file mode 100644
index 00000000..d840a951
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/primary-network-config.bb
@@ -0,0 +1,27 @@
+DESCRIPTION = "Sample network configuration for an Uptane Primary"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+SRC_URI = "\
+ file://27-dhcp-client-external.network \
+ "
+
+FILES_${PN} = "/usr/lib/systemd/network"
+
+PR = "1"
+
+do_install() {
+ install -d ${D}/usr/lib/systemd/network
+ install -m 0644 ${WORKDIR}/27-dhcp-client-external.network ${D}/usr/lib/systemd/network/
+}
+
+PRIMARY_IP ?= "10.0.3.1"
+
+IP_ADDR = "${PRIMARY_IP}"
+CONF_TYPE ?= "${@ 'multihomed' if d.getVar('MACHINE') == 'raspberrypi3' and d.getVar('RPI_WIFI_ENABLE') != '1' else 'static'}"
+
+require network-config.inc
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-test/demo-network-config/secondary-network-config.bb b/external/meta-updater/recipes-test/demo-network-config/secondary-network-config.bb
new file mode 100644
index 00000000..b1d70f1f
--- /dev/null
+++ b/external/meta-updater/recipes-test/demo-network-config/secondary-network-config.bb
@@ -0,0 +1,30 @@
+DESCRIPTION = "Sample network configuration for an Uptane Secondary"
+LICENSE = "MPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MPL-2.0;md5=815ca599c9df247a0c7f619bab123dad"
+
+inherit allarch
+
+# TODO: It configures the 'user' interface in NAT mode and provides an access to public Inet via it
+# which is not desired for Secondary. It cannot be just removed since we get SSH access to Secondary
+# VM via this interface. So, the task is to configure the interface in such way that it does provide access
+# via SSH from a host machine and forbids an access to Inet
+SRC_URI = "\
+ file://27-dhcp-client-external.network \
+ "
+
+FILES_${PN} = "/usr/lib/systemd/network"
+
+PR = "1"
+
+do_install() {
+ install -d ${D}/usr/lib/systemd/network
+ install -m 0644 ${WORKDIR}/27-dhcp-client-external.network ${D}/usr/lib/systemd/network/
+}
+
+SECONDARY_IP ?= "10.0.3.2"
+IP_ADDR = "${SECONDARY_IP}"
+CONF_TYPE = "static"
+
+require network-config.inc
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-test/images/primary-image.bb b/external/meta-updater/recipes-test/images/primary-image.bb
new file mode 100644
index 00000000..ba1dc1fa
--- /dev/null
+++ b/external/meta-updater/recipes-test/images/primary-image.bb
@@ -0,0 +1,16 @@
+include recipes-core/images/core-image-minimal.bb
+
+SUMMARY = "A minimal Uptane Primary image running aktualizr, for testing with a Linux secondary"
+
+LICENSE = "MPL-2.0"
+
+IMAGE_INSTALL_remove = " \
+ virtual/network-configuration \
+ "
+
+IMAGE_INSTALL_append = " \
+ primary-network-config \
+ primary-config \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/recipes-test/images/secondary-image.bb b/external/meta-updater/recipes-test/images/secondary-image.bb
new file mode 100644
index 00000000..27d1e3f9
--- /dev/null
+++ b/external/meta-updater/recipes-test/images/secondary-image.bb
@@ -0,0 +1,29 @@
+include recipes-core/images/core-image-minimal.bb
+
+SUMMARY = "A minimal Uptane Secondary image running aktualizr-secondary"
+
+LICENSE = "MPL-2.0"
+
+SECONDARY_SERIAL_ID ?= ""
+SOTA_HARDWARE_ID ?= "${MACHINE}-sndry"
+
+# Remove default aktualizr primary, and the provisioning configuration (which
+# RDEPENDS on aktualizr)
+IMAGE_INSTALL_remove = " \
+ aktualizr \
+ aktualizr-shared-prov \
+ aktualizr-shared-prov-creds \
+ aktualizr-device-prov \
+ aktualizr-device-prov-creds \
+ aktualizr-device-prov-hsm \
+ aktualizr-uboot-env-rollback \
+ virtual/network-configuration \
+ "
+
+IMAGE_INSTALL_append = " \
+ aktualizr-secondary \
+ secondary-network-config \
+ secondary-config \
+ "
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/external/meta-updater/scripts/ci/Dockerfile.bitbake b/external/meta-updater/scripts/ci/Dockerfile.bitbake
new file mode 100644
index 00000000..c91f94c3
--- /dev/null
+++ b/external/meta-updater/scripts/ci/Dockerfile.bitbake
@@ -0,0 +1,42 @@
+FROM debian:stable
+LABEL Description="Image for bitbaking"
+
+RUN sed -i 's#deb http://deb.debian.org/debian stable main#deb http://deb.debian.org/debian stable main contrib#g' /etc/apt/sources.list
+RUN sed -i 's#deb http://deb.debian.org/debian stable-updates main#deb http://deb.debian.org/debian stable-updates main contrib#g' /etc/apt/sources.list
+RUN apt-get update -q && apt-get install -qy \
+ build-essential \
+ bzip2 \
+ chrpath \
+ cpio \
+ default-jre \
+ diffstat \
+ gawk \
+ gcc-multilib \
+ git-core \
+ iputils-ping \
+ iproute \
+ libpython-dev \
+ libsdl1.2-dev \
+ locales \
+ ovmf \
+ procps \
+ python \
+ python3 \
+ python3-pexpect \
+ qemu \
+ socat \
+ texinfo \
+ unzip \
+ wget \
+ xterm \
+ xz-utils
+
+ARG uid=1000
+ARG gid=1000
+RUN groupadd -g $gid bitbake
+RUN useradd -m -u $uid -g $gid bitbake
+
+RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
+ENV LC_ALL="en_US.UTF-8"
+ENV LANG="en_US.UTF-8"
+ENV LANGUAGE="en_US.UTF-8"
diff --git a/external/meta-updater/scripts/ci/Jenkinsfile.bleeding b/external/meta-updater/scripts/ci/Jenkinsfile.bleeding
new file mode 100644
index 00000000..6d340fde
--- /dev/null
+++ b/external/meta-updater/scripts/ci/Jenkinsfile.bleeding
@@ -0,0 +1,87 @@
+// This CI setup checks out aktualizr, meta-updater and updater-repo and builds
+// master branches whenever a change is pushed to any of these
+
+// define these for docker image creation
+node {
+ // might cause some problems:
+ // https://stackoverflow.com/questions/44805076/setting-build-args-for-dockerfile-agent-using-a-jenkins-declarative-pipeline
+ JENKINS_UID = sh(returnStdout: true, script: 'id -u').trim()
+ JENKINS_GID = sh(returnStdout: true, script: 'id -g').trim()
+}
+
+pipeline {
+ agent any
+ environment {
+ TEST_AKTUALIZR_REMOTE = 'aktualizr'
+ TEST_AKTUALIZR_DIR = 'aktualizr'
+ TEST_AKTUALIZR_BRANCH = 'master'
+ TEST_BITBAKE_COMMON_DIR = "/opt/jenkins/bitbake-common"
+ }
+ stages {
+ stage('checkout') {
+ steps {
+
+ checkout([$class: 'GitSCM',
+ userRemoteConfigs: [
+ [url: 'https://github.com/advancedtelematic/aktualizr', name: 'aktualizr']
+ ],
+ branches: [[name: 'refs/heads/master']],
+ extensions: [
+ [$class: 'DisableRemotePoll'],
+ [$class: 'PruneStaleBranch'],
+ [$class: 'RelativeTargetDirectory',
+ relativeTargetDir: 'aktualizr'
+ ]
+ ],
+ ])
+
+ checkout([$class: 'RepoScm',
+ manifestRepositoryUrl: 'https://github.com/advancedtelematic/updater-repo',
+ manifestBranch: null,
+ manifestFile: 'master.xml',
+ manifestGroup: null,
+ mirrorDir: null,
+ jobs: 0,
+ depth: 0,
+ localManifest: null,
+ destinationDir: 'updater-repo',
+ repoUrl: null,
+ currentBranch: false,
+ resetFirst: true,
+ quiet: false,
+ trace: false,
+ showAllChanges: false,
+ ])
+
+ // ignore bitbake build directories in docker
+ sh 'echo \'build*\' > .dockerignore'
+
+ // override meta-updater commit with currently tested branch
+ sh '''
+ META_UPDATER_COMMIT=$(git rev-parse HEAD)
+ cd updater-repo/meta-updater
+ git checkout $META_UPDATER_COMMIT
+ '''
+ }
+ }
+ stage('build-core-image-minimal') {
+ agent {
+ dockerfile {
+ filename 'scripts/ci/Dockerfile.bitbake'
+ args '-v /opt/jenkins/bitbake-common:/opt/jenkins/bitbake-common'
+ additionalBuildArgs "--build-arg uid=${JENKINS_UID} --build-arg gid=${JENKINS_GID}"
+ reuseNode true
+ }
+ }
+ environment {
+ TEST_AKTUALIZR_CREDENTIALS = credentials('garage-credentials')
+ }
+ steps {
+ sh 'scripts/ci/configure.sh'
+
+ sh 'scripts/ci/build.sh core-image-minimal'
+ }
+ }
+ }
+}
+// vim: set ft=groovy tabstop=2 shiftwidth=2 expandtab:
diff --git a/external/meta-updater/scripts/ci/Jenkinsfile.bleeding-selftest b/external/meta-updater/scripts/ci/Jenkinsfile.bleeding-selftest
new file mode 100644
index 00000000..8c2d1de6
--- /dev/null
+++ b/external/meta-updater/scripts/ci/Jenkinsfile.bleeding-selftest
@@ -0,0 +1,91 @@
+// This CI setup checks out aktualizr, meta-updater and updater-repo and builds
+// master branches whenever a change is pushed to any of these
+
+// define these for docker image creation
+node {
+ // might cause some problems:
+ // https://stackoverflow.com/questions/44805076/setting-build-args-for-dockerfile-agent-using-a-jenkins-declarative-pipeline
+ JENKINS_UID = sh(returnStdout: true, script: 'id -u').trim()
+ JENKINS_GID = sh(returnStdout: true, script: 'id -g').trim()
+}
+
+pipeline {
+ agent {
+ node { label 'bitbake' }
+ }
+ environment {
+ TEST_AKTUALIZR_REMOTE = 'aktualizr'
+ TEST_AKTUALIZR_DIR = 'aktualizr'
+ TEST_AKTUALIZR_BRANCH = 'master'
+ TEST_BITBAKE_COMMON_DIR = "/opt/jenkins/bitbake-common"
+ }
+ stages {
+ stage('checkout') {
+ steps {
+
+ checkout([$class: 'GitSCM',
+ userRemoteConfigs: [
+ [url: 'https://github.com/advancedtelematic/aktualizr', name: 'aktualizr']
+ ],
+ branches: [[name: 'refs/heads/master']],
+ extensions: [
+ [$class: 'DisableRemotePoll'],
+ [$class: 'PruneStaleBranch'],
+ [$class: 'RelativeTargetDirectory',
+ relativeTargetDir: 'aktualizr'
+ ]
+ ],
+ ])
+
+ checkout([$class: 'RepoScm',
+ manifestRepositoryUrl: 'https://github.com/advancedtelematic/updater-repo',
+ manifestBranch: null,
+ manifestFile: 'master.xml',
+ manifestGroup: null,
+ mirrorDir: null,
+ jobs: 0,
+ depth: 0,
+ localManifest: null,
+ destinationDir: 'updater-repo',
+ repoUrl: null,
+ currentBranch: false,
+ resetFirst: true,
+ quiet: false,
+ trace: false,
+ showAllChanges: false,
+ ])
+
+ // ignore bitbake build directories in docker
+ sh 'echo \'build*\' > .dockerignore'
+
+ // override meta-updater commit with currently tested branch
+ sh '''
+ META_UPDATER_COMMIT=$(git rev-parse HEAD)
+ cd updater-repo/meta-updater
+ git checkout $META_UPDATER_COMMIT
+ '''
+ }
+ }
+ stage('build-core-image-minimal+oe-selftest') {
+ agent {
+ dockerfile {
+ filename 'scripts/ci/Dockerfile.bitbake'
+ args '-v /opt/jenkins/bitbake-common:/opt/jenkins/bitbake-common'
+ additionalBuildArgs "--build-arg uid=${JENKINS_UID} --build-arg gid=${JENKINS_GID}"
+ reuseNode true
+ }
+ }
+ environment {
+ TEST_AKTUALIZR_CREDENTIALS = credentials('garage-credentials')
+ }
+ steps {
+ sh 'scripts/ci/configure.sh'
+
+ sh 'scripts/ci/build.sh core-image-minimal'
+
+ sh 'scripts/ci/oe-selftest.sh'
+ }
+ }
+ }
+}
+// vim: set ft=groovy tabstop=2 shiftwidth=2 expandtab:
diff --git a/external/meta-updater/scripts/ci/README.adoc b/external/meta-updater/scripts/ci/README.adoc
new file mode 100644
index 00000000..222982b1
--- /dev/null
+++ b/external/meta-updater/scripts/ci/README.adoc
@@ -0,0 +1,14 @@
+= Jenkins setup for running meta-updater CI
+
+As bitbake is quite resource-hungry, there are some special steps that are
+needed to run Jenkins CI tasks:
+
+- docker should be installed and the `jenkins` unix user should belong to
+ the `docker` group
+- `/opt/jenkins` should exist and have `jenkins:jenkins` permissions, it
+ will be mapped as a volume on the same location in the docker build
+ container
+
+Note that for nodes running Jenkins slaves as a docker container, the
+`/opt/jenkins` directory must exist on the host system as well, with
+permissions matching the user and groupd ids in Jenkins' docker
diff --git a/external/meta-updater/scripts/ci/build.sh b/external/meta-updater/scripts/ci/build.sh
new file mode 100755
index 00000000..62354289
--- /dev/null
+++ b/external/meta-updater/scripts/ci/build.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+TEST_MACHINE=${TEST_MACHINE:-qemux86-64}
+TEST_BUILD_DIR=${TEST_BUILD_DIR:-build}
+TEST_REPO_DIR=${TEST_REPO_DIR:-updater-repo}
+
+IMAGE_NAME=${1:-core-image-minimal}
+
+(
+set +euo pipefail
+set +x
+. "${TEST_REPO_DIR}/meta-updater/scripts/envsetup.sh" "${TEST_MACHINE}" "${TEST_BUILD_DIR}"
+
+bitbake "${IMAGE_NAME}"
+)
diff --git a/external/meta-updater/scripts/ci/configure.sh b/external/meta-updater/scripts/ci/configure.sh
new file mode 100755
index 00000000..960a0cc9
--- /dev/null
+++ b/external/meta-updater/scripts/ci/configure.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+TEST_MACHINE=${TEST_MACHINE:-qemux86-64}
+TEST_BUILD_DIR=${TEST_BUILD_DIR:-build}
+TEST_REPO_DIR=${TEST_REPO_DIR:-updater-repo}
+TEST_BITBAKE_COMMON_DIR=${TEST_BITBAKE_COMMON_DIR:-}
+
+TEST_AKTUALIZR_DIR=${TEST_AKTUALIZR_DIR:-.}
+TEST_AKTUALIZR_BRANCH=${TEST_AKTUALIZR_BRANCH:-master}
+TEST_AKTUALIZR_REV=${TEST_AKTUALIZR_REV:-$(GIT_DIR="$TEST_AKTUALIZR_DIR/.git" git rev-parse "$TEST_AKTUALIZR_REMOTE/$TEST_AKTUALIZR_BRANCH")}
+TEST_AKTUALIZR_CREDENTIALS=${TEST_AKTUALIZR_CREDENTIALS:-}
+
+# move existing conf directory to backup, before generating a new one
+rm -rf "$TEST_BUILD_DIR/conf.old" || true
+mv "$TEST_BUILD_DIR/conf" "$TEST_BUILD_DIR/conf.old" || true
+
+(
+set +euo pipefail
+set +x
+echo ">> Running envsetup.sh"
+. "$TEST_REPO_DIR/meta-updater/scripts/envsetup.sh" "$TEST_MACHINE" "$TEST_BUILD_DIR"
+)
+
+set +x
+
+SITE_CONF="$TEST_BUILD_DIR/conf/site.conf"
+
+echo ">> Set common bitbake config options"
+cat << EOF > "$SITE_CONF"
+SANITY_TESTED_DISTROS = ""
+SSTATE_MIRRORS ?= "file://.* https://bitbake-cache.atsgarage.com/PATH;downloadfilename=PATH"
+IMAGE_FEATURES += "ssh-server-openssh"
+
+EOF
+
+echo ">> Set aktualizr branch in bitbake's config"
+cat << EOF >> "$SITE_CONF"
+SRCREV_pn-aktualizr = "$TEST_AKTUALIZR_REV"
+SRCREV_pn-aktualizr-native = "\${SRCREV_pn-aktualizr}"
+BRANCH_pn-aktualizr = "$TEST_AKTUALIZR_BRANCH"
+BRANCH_pn-aktualizr-native = "\${BRANCH_pn-aktualizr}"
+
+EOF
+
+if [[ -n $TEST_AKTUALIZR_CREDENTIALS ]]; then
+ echo ">> Set aktualizr credentials"
+ cat << EOF >> "$SITE_CONF"
+SOTA_PACKED_CREDENTIALS = "$TEST_AKTUALIZR_CREDENTIALS"
+EOF
+fi
+
+if [[ -n $TEST_BITBAKE_COMMON_DIR ]]; then
+ echo ">> Set caching"
+ SSTATE_DIR="$TEST_BITBAKE_COMMON_DIR/sstate-cache"
+ DL_DIR="$TEST_BITBAKE_COMMON_DIR/downloads"
+ mkdir -p "$SSTATE_DIR" "$DL_DIR"
+
+ cat << EOF >> "$SITE_CONF"
+SSTATE_DIR = "$SSTATE_DIR"
+DL_DIR = "$DL_DIR"
+EOF
+fi
diff --git a/external/meta-updater/scripts/ci/oe-selftest.sh b/external/meta-updater/scripts/ci/oe-selftest.sh
new file mode 100755
index 00000000..3124cce1
--- /dev/null
+++ b/external/meta-updater/scripts/ci/oe-selftest.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# run meta-updater's oe-selftests
+
+set -euo pipefail
+set -x
+
+TEST_MACHINE=${TEST_MACHINE:-qemux86-64}
+TEST_BUILD_DIR=${TEST_BUILD_DIR:-build}
+TEST_REPO_DIR=${TEST_REPO_DIR:-updater-repo}
+
+(
+set +euo pipefail
+set +x
+. "${TEST_REPO_DIR}/meta-updater/scripts/envsetup.sh" "${TEST_MACHINE}" "${TEST_BUILD_DIR}"
+
+oe-selftest -r updater
+)
diff --git a/external/meta-updater/scripts/envsetup.sh b/external/meta-updater/scripts/envsetup.sh
new file mode 100755
index 00000000..5827bc2a
--- /dev/null
+++ b/external/meta-updater/scripts/envsetup.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+SCRIPT="envsetup.sh"
+MACHINE="$1"
+BUILDDIR="build"
+
+[[ "$#" -lt 1 ]] && { echo "Usage: ${SCRIPT} <machine> [builddir]"; return 1; }
+[[ "$#" -eq 2 ]] && { BUILDDIR="$2"; }
+
+# detect if this script is sourced: see http://stackoverflow.com/a/38128348/6255594
+SOURCED=0
+if [ -n "$ZSH_EVAL_CONTEXT" ]; then
+ [[ "$ZSH_EVAL_CONTEXT" =~ :file$ ]] && { SOURCED=1; SOURCEDIR=$(cd "$(dirname -- "$0")" && pwd -P); }
+elif [ -n "$BASH_VERSION" ]; then
+ [[ "$0" != "${BASH_SOURCE[0]}" ]] && { SOURCED=1; SOURCEDIR=$(cd "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P); }
+fi
+
+if [[ $SOURCED -ne 1 ]]; then
+ echo "Error: this script needs to be sourced in a supported shell" >&2
+ echo "Please check that the current shell is bash or zsh and run this script as '. $0 <args>'" >&2
+ exit 1
+fi
+
+METADIR=${METADIR:-${SOURCEDIR}/../..}
+
+if [[ ! -f "${BUILDDIR}/conf/local.conf" ]]; then
+ source "$METADIR/poky/oe-init-build-env" "$BUILDDIR"
+
+ echo "METADIR := \"\${@os.path.abspath('${METADIR}')}\"" >> conf/bblayers.conf
+ cat "${METADIR}/meta-updater/conf/include/bblayers/sota.inc" >> conf/bblayers.conf
+ cat "${METADIR}/meta-updater/conf/include/bblayers/sota_${MACHINE}.inc" >> conf/bblayers.conf
+
+ sed "s/##MACHINE##/$MACHINE/g" "${METADIR}/meta-updater/conf/local.conf.sample.append" >> conf/local.conf
+else
+ source "$METADIR/poky/oe-init-build-env" "$BUILDDIR"
+fi
diff --git a/external/meta-updater/scripts/find_aktualizr_dependencies.sh b/external/meta-updater/scripts/find_aktualizr_dependencies.sh
new file mode 100755
index 00000000..493df800
--- /dev/null
+++ b/external/meta-updater/scripts/find_aktualizr_dependencies.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -euo pipefail
+
+parentdir="$(dirname "$0")"
+
+# Does NOT include garage-sign, anything used only for testing (i.e. strace and
+# gtest), any of the git submodules, all of which are also only used for
+# testing (tuf-test-vectors, isotp-c, ostreesysroot, and HdrHistogram_c), or
+# any other third party modules included directly into the source tree
+# (jsoncpp, open62541, picojson). Also check libp11, dpkg, and systemd since
+# those are common dependencies not enabled by default.
+${parentdir}/find_dependencies.py aktualizr
+${parentdir}/find_dependencies.py aktualizr-shared-prov
+${parentdir}/find_dependencies.py aktualizr-shared-prov-creds
+${parentdir}/find_dependencies.py aktualizr-device-prov
+${parentdir}/find_dependencies.py aktualizr-device-prov-creds
+${parentdir}/find_dependencies.py aktualizr-device-prov-hsm
+${parentdir}/find_dependencies.py aktualizr-auto-reboot
+${parentdir}/find_dependencies.py aktualizr-disable-send-ip
+${parentdir}/find_dependencies.py aktualizr-log-debug
+${parentdir}/find_dependencies.py aktualizr-polling-interval
+${parentdir}/find_dependencies.py libp11
+${parentdir}/find_dependencies.py dpkg
+${parentdir}/find_dependencies.py systemd
+
diff --git a/external/meta-updater/scripts/find_dependencies.py b/external/meta-updater/scripts/find_dependencies.py
new file mode 100755
index 00000000..cffe32bb
--- /dev/null
+++ b/external/meta-updater/scripts/find_dependencies.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python3
+
+from argparse import ArgumentParser
+import os.path
+import sys
+
+scripts_path = os.path.dirname(os.path.realpath(__file__))
+bb_lib_path = os.path.abspath(scripts_path + '/../../poky/bitbake/lib')
+sys.path = sys.path + [bb_lib_path]
+
+import bb.fetch2
+import bb.tinfoil
+
+
+PRINT_PROGRESS = True
+SKIP_BUILD_TOOLS = True
+KNOWN_BUILD_TOOLS = ['virtual/x86_64-poky-linux-gcc', # gcc-cross-x86_64
+ 'virtual/x86_64-poky-linux-compilerlibs', # gcc-runtime
+ 'virtual/i586-poky-linux-gcc', # gcc-cross-i586
+ 'virtual/i586-poky-linux-compilerlibs', # gcc-runtime
+ 'virtual/libc', # glibc
+ 'virtual/libintl', # glibc
+ 'virtual/libiconv', # glibc
+ 'virtual/crypt', # glibc
+ 'autoconf-native',
+ 'automake-native',
+ 'libtool-native',
+ 'gnu-config-native',
+ 'm4-native',
+ 'texinfo-dummy-native',
+ 'gettext-minimal-native',
+ 'libtool-cross',
+ 'gettext-native',
+ 'util-linux-native',
+ 'pkgconfig-native',
+ 'makedepend-native']
+
+
+def get_recipe_info(tinfoil, rn):
+ try:
+ info = tinfoil.get_recipe_info(rn)
+ except Exception:
+ print('Failed to get recipe info for: %s' % rn)
+ return []
+ if not info:
+ print('No recipe info found for: %s' % rn)
+ return []
+ append_files = tinfoil.get_file_appends(info.fn)
+ appends = True
+ data = tinfoil.parse_recipe_file(info.fn, appends, append_files)
+ data.pn = info.pn
+ data.pv = info.pv
+ return data
+
+
+def print_package(manifest_file, data, is_project):
+ src_uri = data.getVar('SRC_URI').split()
+ lic = data.getVar('LICENSE')
+ summary = data.getVar('SUMMARY')
+ homepage = data.getVar('HOMEPAGE')
+ srcrev = data.getVar('SRCREV')
+ branch = data.getVar('BRANCH')
+
+ if is_project:
+ manifest_file.write(' id:\n')
+ else:
+ manifest_file.write('- id:\n')
+ manifest_file.write(' package_manager: "Yocto"\n')
+ manifest_file.write(' namespace: ""\n')
+ manifest_file.write(' name: "%s"\n' % data.pn)
+ manifest_file.write(' version: "%s"\n' % data.pv)
+ manifest_file.write(' declared_lics:\n')
+ manifest_file.write(' - "%s"\n' % lic)
+ if is_project:
+ manifest_file.write(' aliases: []\n')
+ if summary:
+ manifest_file.write(' description: "%s"\n' % summary)
+ else:
+ description = data.getVar('DESCRIPTION')
+ manifest_file.write(' description: "%s"\n' % description)
+ manifest_file.write(' homepage_url: "%s"\n' % homepage)
+ # Binary artifacts almost never exist in Yocto.
+ manifest_file.write(' binary_artifact:\n')
+ manifest_file.write(' url: ""\n')
+ manifest_file.write(' hash: ""\n')
+ manifest_file.write(' hash_algorithm: ""\n')
+ manifest_file.write(' source_artifact:\n')
+ repos = []
+ for src in src_uri:
+ # Strip options.
+ # TODO: ignore files with apply=false?
+ src = src.split(';', maxsplit=1)[0]
+ src_type = src.split('://', maxsplit=1)[0]
+ if src_type == 'file':
+ # TODO: Get full path of patches and other files within the source
+ # repo, not just the filesystem?
+ fetch = bb.fetch2.Fetch([], data)
+ local = fetch.localpath(src)
+ manifest_file.write(' - "%s"\n' % local)
+ else:
+ manifest_file.write(' - "%s"\n' % src)
+ if src_type != 'http' and src_type != 'https' and src_type != 'ftp' and src_type != 'ssh':
+ repos.append(src)
+ if len(repos) > 1:
+ print('Multiple repos for one package are not supported. Package: %s' % info.pn)
+ for repo in repos:
+ vcs_type, url = repo.split('://', maxsplit=1)
+ manifest_file.write(' vcs:\n')
+ if vcs_type == 'gitsm':
+ vcs_type = 'git'
+ manifest_file.write(' type: "%s"\n' % vcs_type)
+ manifest_file.write(' url: "%s"\n' % url)
+ # TODO: Actually support multiple repos here:
+ # TODO: catch and replace AUTOINC?
+ manifest_file.write(' revision: "%s"\n' % srcrev)
+ manifest_file.write(' branch: "%s"\n' % branch)
+
+
+def find_dependencies(manifest_file, tinfoil, assume_provided, recipe_info, packages, rn, order):
+ data = recipe_info[rn]
+ # Filter out packages from the assume_provided list.
+ depends = []
+ for dep in data.depends:
+ if dep not in assume_provided:
+ depends.append(dep)
+
+ if PRINT_PROGRESS:
+ # Print high-order dependencies as a form of logging/progress notifcation.
+ if order == 2:
+ print(rn)
+ if order == 3:
+ print(' ' + rn)
+
+ # order == 1 is for the initial recipe. We've already printed its
+ # information, so skip it.
+ if order > 1:
+ spaces = ' ' * order
+ manifest_file.write('%s- namespace: ""\n' % spaces)
+ manifest_file.write('%s name: "%s"\n' % (spaces, data.pn))
+ manifest_file.write('%s version: "%s"\n' % (spaces, data.pv))
+ if not depends:
+ manifest_file.write('%s dependencies: []\n' % spaces)
+ else:
+ manifest_file.write('%s dependencies:\n' % spaces)
+
+ # First find all dependencies not seen yet to our master list.
+ for dep in depends:
+ if dep not in packages:
+ packages.append(dep)
+ dep_data = get_recipe_info(tinfoil, dep)
+ # Do this once now to reduce the number of bitbake calls.
+ dep_data.depends = dep_data.getVar('DEPENDS').split()
+ recipe_info[dep] = dep_data
+
+ # Then recursively analyze all of the dependencies for the current recipe.
+ for dep in depends:
+ find_dependencies(manifest_file, tinfoil, assume_provided, recipe_info, packages, dep, order + 1)
+
+ if order > 1:
+ manifest_file.write('%s errors: []\n' % spaces)
+
+
+def main():
+ parser = ArgumentParser(description='Find all dependencies of a recipe.')
+ parser.add_argument('recipe', metavar='recipe', help='a recipe to investigate')
+ args = parser.parse_args()
+ rn = args.recipe
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare()
+ # These are the packages that bitbake assumes are provided by the host
+ # system. They do not have recipes, so searching tinfoil for them will
+ # not work. Anyway, by nature they are only build tools and will not be
+ # distributed in an image.
+ assume_provided = tinfoil.config_data.getVar('ASSUME_PROVIDED').split()
+ if SKIP_BUILD_TOOLS:
+ assume_provided.extend(KNOWN_BUILD_TOOLS)
+
+ data = get_recipe_info(tinfoil, rn)
+ if not data:
+ print('Nothing to do!')
+ return
+
+ with open(rn + '-dependencies.yml', "w") as manifest_file:
+ manifest_file.write('project:\n')
+ data.depends = []
+ depends = data.getVar('DEPENDS').split()
+ for dep in depends:
+ if dep not in assume_provided:
+ data.depends.append(dep)
+ print_package(manifest_file, data, is_project=True)
+ manifest_file.write(' scopes:\n')
+ manifest_file.write(' - name: "all"\n')
+ manifest_file.write(' delivered: true\n')
+ if not data.depends:
+ manifest_file.write(' dependencies: []\n')
+ else:
+ manifest_file.write(' dependencies:\n')
+
+ recipe_info = dict([(rn, data)])
+ packages = []
+ find_dependencies(manifest_file, tinfoil, assume_provided, recipe_info, packages, rn, order=1)
+
+ manifest_file.write('packages:\n')
+
+ # Iterate through the list of packages found to print out their full
+ # information. Skip the initial recipe since we already printed it out.
+ for p in packages:
+ if p is not rn:
+ data = recipe_info[p]
+ print_package(manifest_file, data, is_project=False)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/external/meta-updater/scripts/lib/wic/canned-wks/efiimage-sota.wks b/external/meta-updater/scripts/lib/wic/canned-wks/efiimage-sota.wks
new file mode 100644
index 00000000..1e37d96c
--- /dev/null
+++ b/external/meta-updater/scripts/lib/wic/canned-wks/efiimage-sota.wks
@@ -0,0 +1,8 @@
+# short-description: Create an OTA-enabled EFI disk image
+# long-description: Creates an OTA-enabled EFI disk image that the user
+# can directly dd to boot media.
+
+part /boot --source bootimg-efi --sourceparams="loader=grub-efi" --ondisk hda --label msdos --active --align 1024
+part / --source otaimage --ondisk hda --fstype=ext4 --align 1024 --use-uuid
+
+bootloader --ptable gpt --timeout=5 --append="rootfstype=ext4 console=ttyS0,115200 console=tty0" --configfile="grub-ota.cfg"
diff --git a/external/meta-updater/scripts/lib/wic/canned-wks/grub-ota.cfg b/external/meta-updater/scripts/lib/wic/canned-wks/grub-ota.cfg
new file mode 100644
index 00000000..fcd8d70c
--- /dev/null
+++ b/external/meta-updater/scripts/lib/wic/canned-wks/grub-ota.cfg
@@ -0,0 +1,2 @@
+search.fs_label otaroot root
+configfile /boot/loader/grub.cfg
diff --git a/external/meta-updater/scripts/lib/wic/canned-wks/sdimage-sota.wks b/external/meta-updater/scripts/lib/wic/canned-wks/sdimage-sota.wks
new file mode 100644
index 00000000..f3965266
--- /dev/null
+++ b/external/meta-updater/scripts/lib/wic/canned-wks/sdimage-sota.wks
@@ -0,0 +1,7 @@
+# short-description: Create OTA-enabled SD card image
+# long-description: Creates a partitioned SD card image with OSTree
+# physical sysroot as a payload. Boot files are located in the
+# first vfat partition.
+
+part /boot --source bootimg-partition --ondisk mmcblk --fstype=vfat --label boot --active --align 4096 --size 20
+part / --source otaimage --ondisk mmcblk --fstype=ext4 --align 4096
diff --git a/external/meta-updater/scripts/lib/wic/plugins/source/otaimage.py b/external/meta-updater/scripts/lib/wic/plugins/source/otaimage.py
new file mode 100644
index 00000000..1f82af73
--- /dev/null
+++ b/external/meta-updater/scripts/lib/wic/plugins/source/otaimage.py
@@ -0,0 +1,69 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+import logging
+import os
+import sys
+
+from wic import WicError
+from wic.plugins.source.rawcopy import RawCopyPlugin
+from wic.misc import get_bitbake_var
+
+logger = logging.getLogger('wic')
+
+class OTAImagePlugin(RawCopyPlugin):
+ """
+ Add an already existing filesystem image to the partition layout.
+ """
+
+ name = 'otaimage'
+
+ @classmethod
+ def _get_src_file(cls, image_dir_var):
+ """
+ Get OTA image file from image directory variable.
+ """
+ image_dir = get_bitbake_var(image_dir_var)
+ if not image_dir:
+ raise WicError("Couldn't find %s, exiting" % image_dir_var)
+
+ image_file = image_dir + "/" + get_bitbake_var("IMAGE_LINK_NAME") + ".ota-ext4"
+ return image_file if os.path.exists(image_file) else ""
+
+ @classmethod
+ def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
+ oe_builddir, bootimg_dir, kernel_dir,
+ rootfs_dir, native_sysroot):
+ """
+ Called to do the actual content population for a partition i.e. it
+ 'prepares' the partition to be incorporated into the image.
+ """
+
+ src = cls._get_src_file("IMGDEPLOYDIR")
+ if not src:
+ src = cls._get_src_file("DEPLOY_DIR_IMAGE")
+ if not src:
+ raise WicError("Couldn't find ota image in IMGDEPLOYDIR or DEPLOY_DIR_IMAGE, exiting")
+
+ logger.debug('Preparing partition using image %s' % (src))
+ source_params['file'] = src
+
+ super(OTAImagePlugin, cls).do_prepare_partition(part, source_params,
+ cr, cr_workdir, oe_builddir,
+ bootimg_dir, kernel_dir,
+ rootfs_dir, native_sysroot)
+
diff --git a/external/meta-updater/scripts/qa b/external/meta-updater/scripts/qa
new file mode 100755
index 00000000..e5133f0b
--- /dev/null
+++ b/external/meta-updater/scripts/qa
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+
+from subprocess import Popen, PIPE
+from glob import glob
+from os.path import dirname, join, abspath, exists
+from os import chdir
+import signal
+
+root = abspath(dirname(dirname(dirname(__file__))))
+print("Root dir is:" + root)
+
+args = ['bitbake', 'core-image-minimal']
+
+
+class Runner(object):
+ UNKNOWN = 'unknown'
+ PASS = 'pass'
+ FAIL = 'fail'
+ SKIP = 'skipped'
+
+ def __init__(self, dirs):
+ self._dirs = dirs
+ self._child = None
+ self._results = {}
+ for d in dirs:
+ self._results[d] = self.UNKNOWN
+
+ def run(self):
+ for d in self._dirs:
+ chdir(d)
+ if exists(join(d, '.qaskip')):
+ print("Skipping %s because of .qaskip file" % d)
+ self._results[d] = self.SKIP
+ continue
+ print("Building in " + d)
+ self._child = Popen(args=args, cwd=d, stdin=PIPE)
+ retcode = self._child.wait()
+ self._child = None
+ if retcode == 0:
+ self._results[d] = self.PASS
+ else:
+ self._results[d] = self.FAIL
+ print("Error, stopping qa script at %s" % d)
+ break
+ for d, result in self._results.items():
+ print("%20s %s" % (d, result))
+
+ def handle_signal(self, signo, stack_frame):
+ if self._child:
+ self._child
+
+
+def main():
+ dirs = glob(join(root, 'build*'))
+ runner = Runner(dirs)
+ signal.signal(signalnum=signal.SIGINT, handler=runner.handle_signal)
+ runner.run()
+
+if __name__ == "__main__":
+ main()
diff --git a/external/meta-updater/scripts/qemucommand.py b/external/meta-updater/scripts/qemucommand.py
new file mode 100644
index 00000000..3045b454
--- /dev/null
+++ b/external/meta-updater/scripts/qemucommand.py
@@ -0,0 +1,137 @@
+from os.path import exists, join, realpath, abspath
+from os import listdir
+import random
+import socket
+from subprocess import check_output
+
+EXTENSIONS = {
+ 'intel-corei7-64': 'wic',
+ 'qemux86-64': 'ota-ext4'
+}
+
+
+def find_local_port(start_port):
+ """"
+ Find the next free TCP port after 'start_port'.
+ """
+
+ for port in range(start_port, start_port + 10):
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(('', port))
+ return port
+ except socket.error:
+ print("Skipping port %d" % port)
+ finally:
+ s.close()
+ raise Exception("Could not find a free TCP port")
+
+
+def random_mac():
+ """Return a random Ethernet MAC address
+ @link https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml#ethernet-numbers-2
+ """
+ head = "ca:fe:"
+ hex_digits = '0123456789abcdef'
+ tail = ':'.join([random.choice(hex_digits) + random.choice(hex_digits) for _ in range(4)])
+ return head + tail
+
+
+class QemuCommand(object):
+ def __init__(self, args):
+ if args.machine:
+ self.machine = args.machine
+ else:
+ machines = listdir(args.dir)
+ if len(machines) == 1:
+ self.machine = machines[0]
+ else:
+ raise ValueError("Could not autodetect machine type. More than one entry in %s. Maybe --machine qemux86-64?" % args.dir)
+ if args.efi:
+ self.bios = 'OVMF.fd'
+ else:
+ uboot = abspath(join(args.dir, self.machine, 'u-boot-qemux86-64.rom'))
+ if not exists(uboot):
+ raise ValueError("U-Boot image %s does not exist" % uboot)
+ self.bios = uboot
+ if exists(args.imagename):
+ image = args.imagename
+ else:
+ ext = EXTENSIONS.get(self.machine, 'wic')
+ image = join(args.dir, self.machine, '%s-%s.%s' % (args.imagename, self.machine, ext))
+ self.image = realpath(image)
+ if not exists(self.image):
+ raise ValueError("OS image %s does not exist" % self.image)
+ if args.mac:
+ self.mac_address = args.mac
+ else:
+ self.mac_address = random_mac()
+ self.serial_port = find_local_port(8990)
+ self.ssh_port = find_local_port(2222)
+ if args.mem:
+ self.mem = args.mem
+ else:
+ self.mem = "1G"
+ if args.kvm is None:
+ # Autodetect KVM using 'kvm-ok'
+ try:
+ check_output(['kvm-ok'])
+ self.kvm = True
+ except Exception:
+ self.kvm = False
+ else:
+ self.kvm = args.kvm
+ self.gui = not args.no_gui
+ self.gdb = args.gdb
+ self.pcap = args.pcap
+ self.overlay = args.overlay
+ self.secondary_network = args.secondary_network
+
+ def command_line(self):
+ netuser = 'user,hostfwd=tcp:0.0.0.0:%d-:22,restrict=off' % self.ssh_port
+ if self.gdb:
+ netuser += ',hostfwd=tcp:0.0.0.0:2159-:2159'
+ cmdline = [
+ "qemu-system-x86_64",
+ "-bios", self.bios
+ ]
+ if not self.overlay:
+ cmdline += ["-drive", "file=%s,if=ide,format=raw,snapshot=on" % self.image]
+ cmdline += [
+ "-serial", "tcp:127.0.0.1:%d,server,nowait" % self.serial_port,
+ "-m", self.mem,
+ "-usb",
+ "-object", "rng-random,id=rng0,filename=/dev/urandom",
+ "-device", "virtio-rng-pci,rng=rng0",
+ "-device", "usb-tablet",
+ "-show-cursor",
+ "-vga", "std",
+ "-net", netuser,
+ "-net", "nic,macaddr=%s" % self.mac_address
+ ]
+ if self.pcap:
+ cmdline += ['-net', 'dump,file=' + self.pcap]
+ if self.secondary_network:
+ cmdline += [
+ '-netdev', 'socket,id=vlan1,mcast=230.0.0.1:1234,localaddr=127.0.0.1',
+ '-device', 'e1000,netdev=vlan1,mac='+random_mac(),
+ ]
+ if self.gui:
+ cmdline += ["-serial", "stdio"]
+ else:
+ cmdline.append('-nographic')
+ if self.kvm:
+ cmdline += ['-enable-kvm', '-cpu', 'host']
+ else:
+ cmdline += ['-cpu', 'Haswell']
+ if self.overlay:
+ cmdline.append(self.overlay)
+ return cmdline
+
+ def img_command_line(self):
+ cmdline = [
+ "qemu-img", "create",
+ "-o", "backing_file=%s" % self.image,
+ "-f", "qcow2",
+ self.overlay]
+ return cmdline
diff --git a/external/meta-updater/scripts/run-qemu-ota b/external/meta-updater/scripts/run-qemu-ota
new file mode 100755
index 00000000..de632970
--- /dev/null
+++ b/external/meta-updater/scripts/run-qemu-ota
@@ -0,0 +1,74 @@
+#! /usr/bin/env python
+
+from argparse import ArgumentParser
+from subprocess import Popen
+from os.path import exists
+import sys
+from qemucommand import QemuCommand
+
+DEFAULT_DIR = 'tmp/deploy/images'
+
+
+def main():
+ parser = ArgumentParser(description='Run meta-updater image in qemu')
+ parser.add_argument('imagename', default='core-image-minimal', nargs='?',
+ help="Either the name of the bitbake image target, or a path to the image to run")
+ parser.add_argument('mac', default=None, nargs='?')
+ parser.add_argument('--dir', default=DEFAULT_DIR,
+ help='Path to build directory containing the image and u-boot-qemux86-64.rom')
+ parser.add_argument('--efi',
+ help='Boot using UEFI rather than U-Boot. This requires the image to be built with ' +
+ 'OSTREE_BOOTLOADER = "grub" and OVMF.fd firmware to be installed (try "apt install ovmf")',
+ action='store_true')
+ parser.add_argument('--machine', default=None, help="Target MACHINE")
+ kvm_group = parser.add_argument_group()
+ kvm_group.add_argument('--force-kvm', help='Force use of KVM (default is to autodetect)',
+ dest='kvm', action='store_true', default=None)
+ kvm_group.add_argument('--no-kvm', help='Disable KVM in QEMU',
+ dest='kvm', action='store_false')
+ parser.add_argument('--mem', default=None, help="Amount of memory the machine boots with")
+ parser.add_argument('--no-gui', help='Disable GUI', action='store_true')
+ parser.add_argument('--gdb', help='Export gdbserver port 2159 from the image', action='store_true')
+ parser.add_argument('--pcap', default=None, help='Dump all network traffic')
+ parser.add_argument('-o', '--overlay', type=str, metavar='file.cow',
+ help='Use an overlay storage image file. Will be created if it does not exist. ' +
+ 'This option lets you have a persistent image without modifying the underlying image ' +
+ 'file, permitting multiple different persistent machines.')
+ parser.add_argument('--secondary-network', action='store_true', dest='secondary_network',
+ help='Give the image a second network card connected to a virtual network. ' +
+ 'This can be used to test Uptane Primary/Secondary communication.')
+ parser.add_argument('-n', '--dry-run', help='Print qemu command line rather then run it', action='store_true')
+ args = parser.parse_args()
+ try:
+ qemu_command = QemuCommand(args)
+ except ValueError as e:
+ print(e.message)
+ sys.exit(1)
+
+ print("Launching %s with mac address %s" % (args.imagename, qemu_command.mac_address))
+ print("To connect via SSH:")
+ print(" ssh -o StrictHostKeyChecking=no root@localhost -p %d" % qemu_command.ssh_port)
+ print("To connect to the serial console:")
+ print(" nc localhost %d" % qemu_command.serial_port)
+
+ cmdline = qemu_command.command_line()
+ if args.overlay and not exists(args.overlay):
+ print("Image file %s does not yet exist, creating." % args.overlay)
+ img_cmdline = qemu_command.img_command_line()
+ if args.dry_run:
+ print(" ".join(img_cmdline))
+ else:
+ Popen(img_cmdline).wait()
+
+ if args.dry_run:
+ print(" ".join(cmdline))
+ else:
+ s = Popen(cmdline)
+ try:
+ s.wait()
+ except KeyboardInterrupt:
+ pass
+
+
+if __name__ == '__main__':
+ main()