Browse Source

MEN-2793: Merge branch 'next'

This brings the rewrite of mender-convert into master, which will
become mender-convert 2.0.0.

This is essentially a merge with `-s theirs`. I also manually checked
there were no omitted changes from the existing master.

Changelog: None

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>
2.0.x
Kristian Amlie 5 years ago
parent
commit
2c42058a52
No known key found for this signature in database GPG Key ID: F464407C996AF03F
  1. 6
      .dockerignore
  2. 12
      .gitignore
  3. 130
      .gitlab-ci.yml
  4. 5
      .gitmodules
  5. 27
      .travis.yml
  6. 117
      Dockerfile
  7. 1
      LIC_FILES_CHKSUM.sha256
  8. 189
      README.md
  9. 44
      bbb-convert-stage-2.sh
  10. 40
      configs/beaglebone_black_base_config
  11. 9
      configs/beaglebone_black_debian_emmc_config
  12. 8
      configs/beaglebone_black_debian_sdcard_config
  13. 14
      configs/images/raspberrypi3_raspbian_config
  14. 194
      configs/mender_convert_config
  15. 35
      configs/mender_grub_config
  16. 19
      configs/qemux86-64_config
  17. 8
      configs/raspberrypi0w_config
  18. 8
      configs/raspberrypi3_config
  19. 8
      configs/raspberrypi4_config
  20. 83
      configs/raspberrypi_config
  21. 28
      configs/rockpro64_config
  22. 8
      configs/rockpro64_emmc_config
  23. 6
      configs/rockpro64_sd_config
  24. 22
      convert-stage-3.sh
  25. 366
      convert-stage-4.sh
  26. 389
      convert-stage-5.sh
  27. 1
      device-image-shell/.dockerignore
  28. 13
      device-image-shell/Dockerfile
  29. 64
      device-image-shell/README.md
  30. 7
      device-image-shell/docker-build
  31. 58
      device-image-shell/docker-device-image-shell
  32. 38
      device-image-shell/docker-entrypoint.sh
  33. 32
      docker-build
  34. 17
      docker-entrypoint.sh
  35. 20
      docker-mender-convert
  36. 112
      files/init_resize.sh
  37. 13
      files/resizefs.service
  38. 42
      files/resizefs.sh
  39. BIN
      files/uboot_debian_9.4/MLO
  40. BIN
      files/uboot_debian_9.4/u-boot.img
  41. 31
      files/variables.template
  42. 893
      mender-convert
  43. 109
      mender-convert-extract
  44. 888
      mender-convert-functions.sh
  45. 229
      mender-convert-modify
  46. 311
      mender-convert-package
  47. 52
      modules/bootstrap.sh
  48. 22
      modules/config.sh
  49. 176
      modules/disk.sh
  50. 67
      modules/log.sh
  51. 239
      modules/probe.sh
  52. 28
      modules/run.sh
  53. 21
      qemux86_64-convert-stage-2.sh
  54. 1
      requirements-deb.txt
  55. 22
      rockpro64-convert-stage-2.sh
  56. 0
      rootfs_overlay_demo/.gitkeep
  57. 14
      rpi-convert-stage-2.sh
  58. 258
      rpi-convert-stage-5.sh
  59. 16
      scripts/README-run-tests.md
  60. 77
      scripts/bootstrap-rootfs-overlay-demo-server.sh
  61. 67
      scripts/bootstrap-rootfs-overlay-hosted-server.sh
  62. 80
      scripts/bootstrap-rootfs-overlay-production-server.sh
  63. 105
      scripts/test/run-tests.sh
  64. 114
      scripts/test/test-utils.sh
  65. 1
      tests/mender-image-tests

6
.dockerignore

@ -1,3 +1,3 @@
input/
output/
device-image-shell/output
deploy
input
work

12
.gitignore

@ -1,5 +1,7 @@
input/
output/
device-image-shell/output
mendertesting/
integration/
deploy
input
work
rootfs_overlay_demo/*
mender_local_config
*.xml
*.html

130
.gitlab-ci.yml

@ -2,40 +2,128 @@ image: docker:git
variables:
DOCKER_REPOSITORY: mendersoftware/mender-convert
S3_BUCKET_NAME: mender-convert-images
MENDER_ARTIFACT_VERSION: master
MENDER_CLIENT_VERSION: master
RASPBIAN_URL: http://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-09-30/2019-09-26-raspbian-buster-lite.zip
RASPBIAN_NAME: 2019-09-26-raspbian-buster-lite
MENDER_IMAGE_TESTS_REV: master
DEBIAN_FRONTEND: noninteractive
include:
- project: 'Northern.tech/Mender/mendertesting'
file: '.gitlab-ci-check-commits.yml'
- project: 'Northern.tech/Mender/mendertesting'
file: '.gitlab-ci-check-license.yml'
stages:
- test
- build
test:
stage: test
before_script:
- apk add --no-cache bash perl-utils
# Rename the branch we're on, so that it's not in the way for the
# subsequent fetch. It's ok if this fails, it just means we're not on any
# branch.
- git branch -m temp-branch || true
# Git trick: Fetch directly into our local branches instead of remote
# branches.
- git fetch origin 'refs/heads/*:refs/heads/*'
# Get last remaining tags, if any.
- git fetch --tags origin
- git clone http://github.com/mendersoftware/mendertesting
script:
# Check commit compliance.
- mendertesting/check_commits.sh
# Check licenses
- mendertesting/check_license.sh
- convert
- test_acceptance
- publish
build:
stage: build
services:
- docker:dind
script:
- IMAGE_NAME=$DOCKER_REPOSITORY:pr ./docker-build
- IMAGE_NAME=$DOCKER_REPOSITORY:pr ./docker-build --build-arg MENDER_ARTIFACT_VERSION=${MENDER_ARTIFACT_VERSION}
- docker save $DOCKER_REPOSITORY:pr > image.tar
artifacts:
expire_in: 2w
paths:
- image.tar
test_acceptance:
stage: test_acceptance
image: teracy/ubuntu:18.04-dind-18.09.9
services:
- docker:18-dind
tags:
- mender-qa-slave
dependencies:
- build
- convert_raspbian
timeout: 2h
before_script:
# Install dependencies
- apt update
- apt install -qyy bash wget git util-linux mtools python3 python3-pip
gcc python3-dev libffi-dev liblzo2-dev libc-dev libssl-dev make sudo
awscli unzip
# Python3 dependencies
- pip3 install -r https://raw.githubusercontent.com/mendersoftware/meta-mender/master/tests/acceptance/requirements_py3.txt
# Load image under test
- export IMAGE_NAME=$DOCKER_REPOSITORY:pr
- docker load -i image.tar
# Fetch artifacts from temporary S3 bucket
- aws s3 cp s3://mender-gitlab-tmp-storage/$CI_PROJECT_NAME/$CI_PIPELINE_ID/deploy.tar.gz deploy.tar.gz
- tar xzf deploy.tar.gz
# Extract converted Raspbian artifacts
- unxz deploy/raspberrypi-${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}.sdimg.xz
# Set submodule to correct version
- ( cd tests/mender-image-tests && git submodule update --init --remote && git checkout origin/${MENDER_IMAGE_TESTS_REV} )
script:
- ./scripts/test/run-tests.sh --no-pull --prebuilt-image raspberrypi ${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}
- ./scripts/test/run-tests.sh --no-pull --all
artifacts:
expire_in: 2w
when: always
paths:
- results_*.xml
- report_*.html
reports:
junit: results_*.xml
convert_raspbian:
stage: convert
image: teracy/ubuntu:18.04-dind-18.09.9
services:
- docker:18-dind
tags:
- mender-qa-slave
dependencies:
- build
before_script:
- apt update && apt install -yy bash wget unzip awscli
- export IMAGE_NAME=$DOCKER_REPOSITORY:pr
- docker load -i image.tar
- wget -q ${RASPBIAN_URL}
- unzip ${RASPBIAN_NAME}.zip
script:
- echo "MENDER_CLIENT_VERSION=${MENDER_CLIENT_VERSION}" > mender_client_version_config
- env MENDER_ARTIFACT_NAME=${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}
./docker-mender-convert -d ${RASPBIAN_NAME}.img
-c configs/raspberrypi3_config
-c configs/images/raspberrypi3_raspbian_config
-c mender_client_version_config
# Upload to temporary S3 bucket
- tar czf deploy.tar.gz deploy
- aws s3 cp deploy.tar.gz s3://mender-gitlab-tmp-storage/$CI_PROJECT_NAME/$CI_PIPELINE_ID/deploy.tar.gz
publish:s3:
when: manual
stage: publish
image: debian:buster
before_script:
- apt update && apt install -yyq awscli
# Fetch artifacts from temporary S3 bucket
- aws s3 cp s3://mender-gitlab-tmp-storage/$CI_PROJECT_NAME/$CI_PIPELINE_ID/deploy.tar.gz deploy.tar.gz
- tar xzf deploy.tar.gz
script:
- echo "Publishing ${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}.img.xz version to S3"
# Prepare high privilege S3 keys (the base keys are for the tmp storage only)
- export AWS_ACCESS_KEY_ID=$PUBLISH_AWS_ACCESS_KEY_ID
- export AWS_SECRET_ACCESS_KEY=$PUBLISH_AWS_SECRET_ACCESS_KEY
- aws s3 cp deploy/raspberrypi-${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}.sdimg.xz
s3://$S3_BUCKET_NAME/${RASPBIAN_NAME}/arm/${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}.img.xz
- aws s3api put-object-acl --acl public-read --bucket $S3_BUCKET_NAME
--key ${RASPBIAN_NAME}/arm/${RASPBIAN_NAME}-mender-${MENDER_CLIENT_VERSION}.img.xz

5
.gitmodules

@ -0,0 +1,5 @@
[submodule "tests/mender-image-tests"]
path = tests/mender-image-tests
url = https://github.com/mendersoftware/mender-image-tests
branch = master
ignore = all

27
.travis.yml

@ -1,27 +0,0 @@
language: minimal
# Disable git shallow clone. We need full history for validating copyright year of each file.
git:
depth: false
dist: trusty
before_script:
- git clone git://github.com/mendersoftware/mendertesting
# Rename the branch we're on, so that it's not in the way for the
# subsequent fetch. It's ok if this fails, it just means we're not on any
# branch.
- git branch -m temp-branch || true
# Git trick: Fetch directly into our local branches instead of remote
# branches.
- git fetch origin 'refs/heads/*:refs/heads/*'
# Get last remaining tags, if any.
- git fetch --tags origin
script:
# Check commit compliance.
- mendertesting/check_commits.sh
# Check licenses
- mendertesting/check_license.sh
- ./docker-build

117
Dockerfile

@ -1,100 +1,55 @@
FROM ubuntu:18.04
# Build pxz in separate image to avoid big image size
FROM ubuntu:19.04 AS build
RUN apt-get update && apt-get install -y \
build-essential \
git \
liblzma-dev
# Parallel xz (LZMA) compression
RUN git clone https://github.com/jnovy/pxz.git /root/pxz
RUN cd /root/pxz && make
FROM ubuntu:19.04
ARG MENDER_ARTIFACT_VERSION=3.2.0
ARG GOLANG_VERSION=1.11.2
ARG MENDER_ARTIFACT_VERSION=3.2.1
RUN apt-get update && apt-get install -y \
kpartx \
bison \
flex \
mtools \
# For 'ar' command to unpack .deb
binutils \
xz-utils \
# to be able to detect file system types of extracted images
file \
# to copy files between rootfs directories
rsync \
# to generate partition table
parted \
mtd-utils \
# mkfs.ext4 and family
e2fsprogs \
u-boot-tools \
# mkfs.xfs and family
xfsprogs \
# Parallel gzip compression
pigz \
device-tree-compiler \
autoconf \
autotools-dev \
libtool \
pkg-config \
python \
jq \
# for mender-convert to run (mkfs.vfat is required for boot partition)
sudo \
# mkfs.vfat (required for boot partition)
dosfstools \
# to compile U-Boot
bc \
# to download mender-artifact
wget \
# to download mender-convert and U-Boot sources
# to download mender-grub-env
git \
# for arm64 support
gcc-aarch64-linux-gnu
# to compile mender-grub-env
make \
# to get rid of 'sh: 1: udevadm: not found' errors triggered by parted
udev \
# to create bmap index file (MENDER_USE_BMAP)
bmap-tools \
# needed to run pxz
libgomp1
# Disable sanity checks made by mtools. These checks reject copy/paste operations on converted disk images.
RUN echo "mtools_skip_check=1" >> $HOME/.mtoolsrc
# To provide support for Raspberry Pi Zero W a toolchain tuned for ARMv6 architecture must be used.
# https://tracker.mender.io/browse/MEN-2399
# Assumes $(pwd) is /
RUN wget -nc -q https://toolchains.bootlin.com/downloads/releases/toolchains/armv6-eabihf/tarballs/armv6-eabihf--glibc--stable-2018.11-1.tar.bz2 \
&& tar -xjf armv6-eabihf--glibc--stable-2018.11-1.tar.bz2 \
&& rm armv6-eabihf--glibc--stable-2018.11-1.tar.bz2 \
&& echo 'export PATH=$PATH:/armv6-eabihf--glibc--stable-2018.11-1/bin' >> /root/.bashrc
COPY --from=build /root/pxz/pxz /usr/bin/pxz
RUN wget -q -O /usr/bin/mender-artifact https://d1b0l86ne08fsf.cloudfront.net/mender-artifact/$MENDER_ARTIFACT_VERSION/linux/mender-artifact \
&& chmod +x /usr/bin/mender-artifact
# Golang environment, for cross-compiling the Mender client
RUN wget https://dl.google.com/go/go$GOLANG_VERSION.linux-amd64.tar.gz \
&& tar -C /usr/local -xzf go$GOLANG_VERSION.linux-amd64.tar.gz \
&& echo 'export PATH=$PATH:/usr/local/go/bin' >> /root/.bashrc
ENV PATH "$PATH:/usr/local/go/bin:/armv6-eabihf--glibc--stable-2018.11-1/bin"
ENV GOPATH "/root/go"
# Download Mender client
ARG mender_client_version
RUN test -n "$mender_client_version" || (echo "Argument 'mender_client_version' is mandatory." && exit 1)
ENV MENDER_CLIENT_VERSION=$mender_client_version
RUN go get -d github.com/mendersoftware/mender
WORKDIR $GOPATH/src/github.com/mendersoftware/mender
RUN git checkout $MENDER_CLIENT_VERSION
# Toolchain configuration
ARG toolchain_host
RUN test -n "$toolchain_host" || (echo "Argument 'toolchain_host' is mandatory." && exit 1)
ENV TOOLCHAIN_HOST=${toolchain_host}
ARG go_flags
RUN test -n "$go_flags" || (echo "Argument 'go_flags' is mandatory." && exit 1)
ENV GO_FLAGS=$go_flags
RUN test -n "$mender_client_version" || (echo "Argument 'mender_client_version' is mandatory." && exit 1)
ENV MENDER_CLIENT_VERSION=$mender_client_version
ENV CC "${TOOLCHAIN_HOST}-gcc"
# Build liblzma from source
RUN wget -q https://tukaani.org/xz/xz-5.2.4.tar.gz \
&& tar -C /root -xzf xz-5.2.4.tar.gz \
&& cd /root/xz-5.2.4 \
&& ./configure --host=${TOOLCHAIN_HOST} --prefix=/root/xz-5.2.4/install \
&& make \
&& make install
ENV LIBLZMA_INSTALL_PATH "/root/xz-5.2.4/install"
# NOTE: we are assuming generic ARM board here, needs to be extended later
RUN env CGO_ENABLED=1 \
CGO_CFLAGS="-I${LIBLZMA_INSTALL_PATH}/include" \
CGO_LDFLAGS="-L${LIBLZMA_INSTALL_PATH}/lib" \
CC=$CC \
GOOS=linux \
${GO_FLAGS} make build
# allow us to keep original PATH variables when sudoing
RUN echo "Defaults secure_path=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:$PATH\"" > /etc/sudoers.d/secure_path_override
RUN chmod 0440 /etc/sudoers.d/secure_path_override

1
LIC_FILES_CHKSUM.sha256

@ -1,2 +1 @@
beb140be4cd64599bedc691a55b2729c9cc611a4b9d6ec44e01270105daf18a2 LICENSE
beb140be4cd64599bedc691a55b2729c9cc611a4b9d6ec44e01270105daf18a2 mendertesting/LICENSE

189
README.md

@ -1,124 +1,167 @@
[![Build Status](https://travis-ci.com/mendersoftware/mender-convert.svg?branch=master)](https://travis-ci.com/mendersoftware/mender-convert)
[![Build Status](https://gitlab.com/Northern.tech/Mender/mender-convert/badges/next/pipeline.svg)](https://gitlab.com/Northern.tech/Mender/mender-convert/pipelines)
mender-convert
==============
# mender-convert
Mender is an open source over-the-air (OTA) software updater for embedded Linux devices. Mender comprises a client running at the embedded device, as well as a server that manages deployments across many devices.
Mender is an open source over-the-air (OTA) software updater for embedded Linux
devices. Mender comprises a client running at the embedded device, as well as a
server that manages deployments across many devices.
This repository contains mender-convert, which is used to convert pre-built disk images (Debian, Ubuntu, Raspbian, etc) to a Mender compatible
image by restructuring partition table and injecting the necessary files.
This repository contains mender-convert, which is used to convert pre-built disk
images (Debian, Ubuntu, Raspbian, etc) to a Mender compatible image by
restructuring partition table and injecting the necessary files.
Currently official Raspberry Pi 3 and BeagleBone Black images are supported and this will be extended.
For a full list of tested devices and images please visit [Mender
Hub](https://hub.mender.io/c/board-integrations/debian-family). If your device
and image combination is not listed as supported, this does not necessarily mean
that it will not work, it probably just means that no one has tested and
reported it back and usually only small tweaks are necessary to get this running
on your device.
-------------------------------------------------------------------------------
![Mender logo](https://github.com/mendersoftware/mender/raw/master/mender_logo.png)
## Getting started
To start using Mender, we recommend that you begin with the Getting started
section in [the Mender documentation](https://docs.mender.io/).
For more detailed information about `mender-convert` please visit the
[Debian family](https://docs.mender.io/artifacts/debian-family) section in
[the Mender documentation](https://docs.mender.io/).
## Docker environment for mender-convert
In order to correctly set up partitions and bootloaders, mender-convert has many dependencies,
and their version and name vary between Linux distributions.
### Prepare image and configuration
To make using mender-convert easier, a reference setup using a Ubuntu 18.04 Docker container
is provided.
Download the raw Raspberry Pi disk image into a subdirectory input:
You need to [install Docker Engine](https://docs.docker.com/install) to use this environment.
```bash
mkdir -p input
cd input
wget https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-06-24/2019-06-20-raspbian-buster-lite.zip
```
Extract the raw Raspberry Pi disk image:
### Build the mender-convert container image
```bash
unzip 2019-06-20-raspbian-buster-lite.zip
INPUT_DISK_IMAGE=$(ls *raspbian-buster*.img)
cd ..
```
To build a container based on Ubuntu 18.04 with all required dependencies for mender-convert,
copy this directory to your workstation and change the current directory to it.
Bootstrap the demo rootfs overlay that is configured to connect to
a Mender server with polling intervals set appropriately for
demonstration purposes. There are three scripts here to support
the Mender demo server, the Mender production server, and Mender
Professional.
Then run
**NOTE!** Only run one of these three steps depending on the server
type you are implementing.
#### Using the [Mender demo server](https://docs.mender.io/getting-started/on-premise-installation/create-a-test-environment)
```bash
./docker-build
./scripts/bootstrap-rootfs-overlay-demo-server.sh \
--output-dir ${PWD}/rootfs_overlay_demo \
--server-ip 192.168.1.1
```
This will create a container image you can use to run mender-convert.
#### Using the [Mender production server](https://docs.mender.io/administration/production-installation)
```bash
./scripts/bootstrap-rootfs-overlay-production-server.sh \
--output-dir ${PWD}/rootfs_overlay_demo \
--server-url https://foobar.mender.io \
[ --server-cert ~/server.crt ]
```
#### Using [Mender Professional](https://mender.io/products/mender-professional)
```bash
./scripts/bootstrap-rootfs-overlay-hosted-server.sh \
--output-dir ${PWD}/rootfs_overlay_demo \
--tenant-token "Paste token from Mender Professional"
```
### Use the mender-convert container image
Create a directory `input` under the directory where you copied these files (`docker-build`, `docker-mender-convert`, etc.):
### Docker environment for mender-convert
To make using mender-convert easier, a reference setup using a Docker
container is provided.
You need to [install Docker Engine](https://docs.docker.com/install) to use
this environment.
```bash
mkdir input
```
Then put your raw disk image into `input/`, e.g.
#### Build the mender-convert container image
Build a container with all required dependencies for `mender-convert`:
```bash
mv ~/Downloads/2018-11-13-raspbian-stretch.img input/2018-11-13-raspbian-stretch.img
./docker-build
```
You can run mender-convert from inside the container with your desired options, e.g.
This will create a container image with the name `mender-convert` which you can
use to run `mender-convert` without polluting your host environment with the
necessary dependencies.
#### Use the mender-convert container image
Run mender-convert from inside the container with your desired options, e.g.
```bash
DEVICE_TYPE="raspberrypi3"
RAW_DISK_IMAGE="input/2018-11-13-raspbian-stretch.img"
ARTIFACT_NAME="2018-11-13-raspbian-stretch"
MENDER_DISK_IMAGE="2018-11-13-raspbian-stretch.sdimg"
TENANT_TOKEN="<INSERT-TOKEN-FROM Hosted Mender>"
./docker-mender-convert from-raw-disk-image \
--raw-disk-image $RAW_DISK_IMAGE \
--mender-disk-image $MENDER_DISK_IMAGE \
--device-type $DEVICE_TYPE \
--artifact-name $ARTIFACT_NAME \
--bootloader-toolchain arm-buildroot-linux-gnueabihf \
--server-url "https://hosted.mender.io" \
--tenant-token $TENANT_TOKEN
MENDER_ARTIFACT_NAME=release-1 ./docker-mender-convert \
--disk-image input/$INPUT_DISK_IMAGE \
--config configs/raspberrypi3_config \
--overlay rootfs_overlay_demo/
```
By default conversion in containter uses GCC 7.3.0 bootlin toolchain tuned for
ARMv6 architecture (especially for ARM1176(F)-S single-core processor).
The aim of that is to provide a support for the Raspberry Pi Zero W development board.
Conversion will take 10-30 minutes, depending on image size and resources
available. You can watch `work/convert.log` for progress and diagnostics
information.
ARMv7 is backward compatible with ARMv6, so binaries compiled for ARMv6 should also work on ARMv7.
After it finishes, you can find your images in the `deploy` directory on your
host machine!
Note that the default Mender client is the latest stable and cross-compiled for generic ARM boards,
which should work well in most cases. If you would like to use a different Mender client,
place it in `input/` and adjust the `--mender-client` argument.
## Using mender-convert without Docker
Conversion will take 10-15 minutes, depending on your storage and resources available.
You can watch `output/build.log` for progress and diagnostics information.
In order to be able to manipulate and create filesystem and disk images,
mender-convert has a few dependencies, and their version and name vary between
Linux distributions. Here is an example of how to install the dependencies on a
Debian based distribution:
After it finishes, you can find your images in the `output` directory on your host machine!
```bash
sudo apt install $(cat requirements-deb.txt)
```
Start the conversion process with:
### Known issues
* An issue for `Raspberry Pi Zero W` has been spotted with the `mender-convert` tool version 1.1.0.
After an initial boot, having last partition resized to the end of the SD card, the correct device
tree cannot be found. As a result the boot cannot succeed.
For more information and current status, see [issue tracker](https://tracker.mender.io/browse/MEN-2436).
* If building U-boot fails with:
```
D scripts/Kconfig
input in flex scanner failed
....
include/linux/kconfig.h:4:32: fatal error: generated/autoconf.h: No such file or directory
#include <generated/autoconf.h>
```bash
MENDER_ARTIFACT_NAME=release-1 ./mender-convert \
--disk-image input/$INPUT_DISK_IMAGE \
--config configs/raspberrypi3_config \
--overlay rootfs_overlay_demo/
```
you might be using a case-sensitive filesystem which is not supported. Case-sensitive filesystems are typically used on OSX (Mac) and Windows but you can also run in to this on Linux if running on a NTFS formatted partition.
For details see this [discussion](https://hub.mender.io/t/raspberry-pi-3-model-b-b-raspbian/140/10)
**NOTE!** You will be prompted to enter `sudo` password during the conversion
process. This is required to be able to loopback mount images and for modifying
them. Our recommendation is to use the provided Docker container, to run the
tool in a isolated environment.
-------------------------------------------------------------------------------
## Contributing
We welcome and ask for your contribution. If you would like to contribute to Mender, please read our guide on how to best get started [contributing code or documentation](https://github.com/mendersoftware/mender/blob/master/CONTRIBUTING.md).
We welcome and ask for your contribution. If you would like to contribute to
Mender, please read our guide on how to best get started
[contributing code or documentation](https://github.com/mendersoftware/mender/blob/master/CONTRIBUTING.md).
## License
Mender is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/mendersoftware/mender-crossbuild/blob/master/LICENSE) for the full license text.
Mender is licensed under the Apache License, Version 2.0. See
[LICENSE](https://github.com/mendersoftware/mender-convert/blob/master/LICENSE)
for the full license text.
## Security disclosure
@ -127,6 +170,7 @@ security, please disclose the information by sending an email to
[security@mender.io](security@mender.io). Please do not create a new public
issue. We thank you in advance for your cooperation.
## Connect with us
* Join the [Mender Hub discussion forum](https://hub.mender.io)
@ -136,3 +180,12 @@ issue. We thank you in advance for your cooperation.
* Create an issue in the [bugtracker](https://tracker.mender.io/projects/MEN)
* Email us at [contact@mender.io](mailto:contact@mender.io)
* Connect to the [#mender IRC channel on Freenode](http://webchat.freenode.net/?channels=mender)
## Authors
Mender was created by the team at [Northern.tech AS](https://northern.tech),
with many contributions from the community. Thanks
[everyone](https://github.com/mendersoftware/mender/graphs/contributors)!
[Mender](https://mender.io) is sponsored by [Northern.tech AS](https://northern.tech).

44
bbb-convert-stage-2.sh

@ -1,44 +0,0 @@
#!/bin/bash
output_dir=$1
boot_mapping=$2
embedded_rootfs_dir=$3
uboot_backup_dir=${embedded_rootfs_dir}/opt/backup/uboot
build_log=$output_dir/build.log
[ ! -f $output_dir/boot.vfat ] && \
{ log "Error: extracted boot partition not found. Aborting."; exit 1; }
[ ! -d "${embedded_rootfs_dir}" ] && \
{ log "Error: embedded content not mounted."; exit 1; }
[[ ! -f $uboot_backup_dir/MLO || ! -f $uboot_backup_dir/u-boot.img ]] && \
{ log "Error: cannot find U-Boot related files."; exit 1; }
cat <<- 'EOF' | sudo tee --append ${output_dir}/uEnv.txt 2>&1 >/dev/null
loadaddr=0x82000000
fdtaddr=0x88000000
rdaddr=0x88080000
initrd_high=0xffffffff
fdt_high=0xffffffff
loadximage=echo debug: [/boot/vmlinuz-${uname_r}] ... ; load mmc 0:2 ${loadaddr} /boot/vmlinuz-${uname_r}
loadxfdt=echo debug: [/boot/dtbs/${uname_r}/${fdtfile}] ... ;load mmc 0:2 ${fdtaddr} /boot/dtbs/${uname_r}/${fdtfile}
loadxrd=echo debug: [/boot/initrd.img-${uname_r}] ... ; load mmc 0:2 ${rdaddr} /boot/initrd.img-${uname_r}; setenv rdsize ${filesize}
loaduEnvtxt=load mmc 0:2 ${loadaddr} /boot/uEnv.txt ; env import -t ${loadaddr} ${filesize};
check_dtb=if test -n ${dtb}; then setenv fdtfile ${dtb};fi;
loadall=run loaduEnvtxt; run check_dtb; run loadximage; run loadxrd; run loadxfdt;
mmcargs=setenv bootargs console=tty0 console=${console} ${optargs} ${cape_disable} ${cape_enable} root=/dev/mmcblk0p2 rootfstype=${mmcrootfstype} ${cmdline}
uenvcmd=run loadall; run mmcargs; echo debug: [${bootargs}] ... ; echo debug: [bootz ${loadaddr} ${rdaddr}:${rdsize} ${fdtaddr}] ... ; bootz ${loadaddr} ${rdaddr}:${rdsize} ${fdtaddr};
EOF
mcopy -o -i ${output_dir}/boot.vfat -s ${output_dir}/uEnv.txt ::uEnv.txt
mcopy -o -i ${output_dir}/boot.vfat -s ${uboot_backup_dir}/MLO ::MLO
mcopy -o -i ${output_dir}/boot.vfat -s ${uboot_backup_dir}/u-boot.img ::u-boot.img
sudo dd if=${output_dir}/boot.vfat of=/dev/mapper/${boot_mapping} bs=1M conv=sparse >> "$build_log" 2>&1
log "\tDone."
exit 0

40
configs/beaglebone_black_base_config

@ -0,0 +1,40 @@
# Binaries generated with the following script:
#
# https://github.com/drewmoseley/mender-convert-integration-scripts/blob/master/build-uboot-bbb.sh
#
# There are reported issues with GRUB bootloader integration, fallback to U-boot.
MENDER_GRUB_EFI_INTEGRATION=n
# We will write a modified bootloader
MENDER_COPY_BOOT_GAP=n
# 4MB alignment
MENDER_PARTITION_ALIGNMENT="4194304"
BEAGLEBONE_BLACK_BINARIES_URL="${MENDER_STORAGE_URL}/mender-convert/beaglebone/${BEAGLEBONE_BLACK_BINARIES}"
function platform_modify() {
mkdir -p work/bbb/binaries
run_and_log_cmd "wget -q ${BEAGLEBONE_BLACK_BINARIES_URL} -P work/bbb/binaries"
run_and_log_cmd "tar xvf work/bbb/binaries/${BEAGLEBONE_BLACK_BINARIES} -C work/bbb/binaries"
# Mask udisks2.service, otherwise it will mount the inactive part and we
# might write an update while it is mounted which often result in
# corruptions.
#
# TODO: Find a way to only blacklist mmcblk0pX devices instead of masking
# the service.
run_and_log_cmd "sudo ln -sf /dev/null work/rootfs/etc/systemd/system/udisks2.service"
# Place u-boot and MLO into rootfs/boot
run_and_log_cmd "sudo mkdir -p work/rootfs/boot"
run_and_log_cmd "sudo cp work/bbb/binaries/MLO work/boot/"
run_and_log_cmd "sudo cp work/bbb/binaries/u-boot.img work/boot/"
run_and_log_cmd "sudo cp work/bbb/binaries/fw_env.config work/rootfs/etc/"
run_and_log_cmd "sudo cp work/bbb/binaries/uboot-git-log.txt work/boot"
run_and_log_cmd "sudo install -m 755 work/bbb/binaries/fw_printenv work/rootfs/sbin/fw_printenv"
run_and_log_cmd "sudo ln -fs /sbin/fw_printenv work/rootfs/sbin/fw_setenv"
}

9
configs/beaglebone_black_debian_emmc_config

@ -0,0 +1,9 @@
# Binaries generated with the following script:
#
# https://github.com/drewmoseley/mender-convert-integration-scripts/blob/master/build-uboot-bbb.sh
#
BEAGLEBONE_BLACK_BINARIES="beaglebone-black-integration-debian-emmc-2018.07.004.tar"
source configs/beaglebone_black_base_config
MENDER_STORAGE_TOTAL_SIZE_MB="3648"
MENDER_DEVICE_TYPE="beaglebone-emmc"
MENDER_STORAGE_DEVICE="/dev/mmcblk1p"

8
configs/beaglebone_black_debian_sdcard_config

@ -0,0 +1,8 @@
# Binaries generated with the following script:
#
# https://github.com/drewmoseley/mender-convert-integration-scripts/blob/master/build-uboot-bbb.sh
#
BEAGLEBONE_BLACK_BINARIES="beaglebone-black-integration-debian-sdcard-2018.07.004.tar"
source configs/beaglebone_black_base_config
MENDER_DEVICE_TYPE="beaglebone-sdcard"
MENDER_STORAGE_DEVICE="/dev/mmcblk0p"

14
configs/images/raspberrypi3_raspbian_config

@ -0,0 +1,14 @@
# Real life SD cards typically have less than they advertise. First off, they
# often use a base of 1000 instead of 1024, and even then they are often smaller
# than advertised. The number below is based on a conservative target of 3.9GB
# (that's mathematical GB, base 1000), converted to MiB, rounding down.
MENDER_STORAGE_TOTAL_SIZE_MB=3719
# Use all there is, which gets us almost, but not quite, to 500MiB free space
# (about 480MiB at the time of writing).
IMAGE_ROOTFS_SIZE=-1
IMAGE_ROOTFS_EXTRA_SPACE=0
IMAGE_OVERHEAD_FACTOR=1.0
# Best compression there is!
MENDER_COMPRESS_DISK_IMAGE=lzma

194
configs/mender_convert_config

@ -0,0 +1,194 @@
# This is the default configuration used by mender-convert. You can override
# any option specified here by providing your own configuration file using the
# '--config' argument.
#
# NOTE! This file will always be sourced.
# Compress generated disk image
#
# This is useful when you have large disk images, compressing them
# makes it easier to transfer them between e.g an build server and a local
# machine, and obviously saves space.
#
# Possible values are:
# 'none' - No compression
# 'gzip' - Compress with gzip
# 'lzma' - Compress with xz (LZMA)
MENDER_COMPRESS_DISK_IMAGE=gzip
# Compression algorithm for Mender Artifact
#
# Supported values are: lzma, gzip (default), none
#
# LZMA will produce a smaller Mender Artifact (2-3x) but will significantly
# increase time spent generating the Mender Artifact (10x)
MENDER_ARTIFACT_COMPRESSION="gzip"
# Enable Mender systemd server
#
# You want this enabled if you want the Mender client to operate in managed mode
# and connect to a server. If you are not interested connecting to a server
# and will only be running standalone mode updates, then you can safely disable
# this.
MENDER_ENABLE_SYSTEMD=y
# Set the fstab options for mounting the data partition
MENDER_DATA_PART_FSTAB_OPTS="defaults"
# The file system will be grown to occupy the full block device. If the file
# system is already at maximum size, no action will be performed.
#
# This feature is utilizing x−systemd.growfs and will only work on systemd 242
# and newer and nothing will be done if an older systemd version is used.
MENDER_DATA_PART_GROWFS=y
# The name of the image or update that will be built. This is what the device
# will report that it is running, and different updates must have different
# names.
#
# This variable must be defined or the build will fail, but you should not
# set it in a configuration file and instead it should be an input to the
# mender-convert tool, e.g
#
# MENDER_ARTIFACT_NAME="release-1" ./mender-convert
#
#MENDER_ARTIFACT_NAME=""
# A string that defines the type of device this image will be installed on and
# this value is used for compatibility checks between Mender Artifact and
# what the device is reporting.
#
# You can define multiple types by providing a space separated string of device
# types.
#
# It defaults to the content of '/etc/hostname', which is extracted from the
# input disk image
MENDER_DEVICE_TYPE=""
# Total size of the physical storage medium that mender partitioned images
# will be written to, expressed in MiB. The size of rootfs partition will be
# calculated automatically by subtracting the sizes of boot
# (see MENDER_BOOT_PART_SIZE_MB) and data partitions
# (see MENDER_DATA_PART_SIZE_MB).
#
MENDER_STORAGE_TOTAL_SIZE_MB="8192"
# The size of the boot partition in the generated .biosimg, .sdimg or .uefiimg
# file.
MENDER_BOOT_PART_SIZE_MB="40"
# The size of the persistent data partition in the generated .biosimg, .sdimg
# or .uefiimg file.
#
# You rarely need to increase this number as this partition will be expanded
# on first boot to occupy the renaming free blocks on the physical storage.
MENDER_DATA_PART_SIZE_MB="128"
# Alignment of partitions used when building partitioned images, expressed in
# bytes
#
# Default is 8MB
MENDER_PARTITION_ALIGNMENT="8388608"
# Mender client version
#
# This is used to fetch the correct binaries
MENDER_CLIENT_VERSION="master"
# File storage, containing binary files, do not modify this unless you know
# what you are doing.
MENDER_STORAGE_URL="https://d1b0l86ne08fsf.cloudfront.net"
# Mender GitHub organization URL prefix
MENDER_GITHUB_ORG="https://github.com/mendersoftware"
# Device file corresponding to the root filesystem partitions, without index.
MENDER_STORAGE_DEVICE=/dev/mmcblk0p
# Partition index of boot partition
MENDER_BOOT_PART_INDEX="1"
# Partition index of root filesystem A
MENDER_ROOTFS_PART_A_INDEX="2"
# Partition index of root filesystem B
MENDER_ROOTFS_PART_B_INDEX="3"
# Partition index of persistent data partition
MENDER_DATA_PART_INDEX="4"
# Basename of DTB that should be loaded by the bootloader.
MENDER_KERNEL_DEVICETREE=kernel.dtb
# Generate bmap index files
#
# https://github.com/intel/bmap-tools
MENDER_USE_BMAP="n"
# Copy the boot gap, which is the area from sector 1 until first parition,
# this is done because some images might contain the bootloader embedded here
# and we need to copy the bootloader to the output image.
#
# In most cases you would like this enabled.
MENDER_COPY_BOOT_GAP="y"
# The size of each of the two rootfs filesystems, in KiB. If this is 0,
# mender-convert will use the size of the filesystem content as a basis. If the
# value is -1, mender-convert will use the maximum size that will fit inside the
# created partition. The size is further modified by IMAGE_ROOTFS_EXTRA_SPACE
# and IMAGE_OVERHEAD_FACTOR.
#
# This variable directly mirrors the variable from the Yocto Project, which is
# why it is missing a "MENDER_" prefix.
IMAGE_ROOTFS_SIZE="0"
# The amount of extra free space requested on the rootfs, in KiB. This is added
# to the value of IMAGE_ROOTFS_SIZE. The size is further modified by
# IMAGE_OVERHEAD_FACTOR.
#
# Note that due to reserved space for the root user on the filesystem, "df" may
# report a significantly lower number than requested. A more accurate number can
# be fetched using for example "dumpe2fs" and looking for the "Free blocks"
# field, but even this value is usually going to be lower than requested due to
# meta data on the filesystem.
#
# This variable directly mirrors the variable from the Yocto Project, which is
# why it is missing a "MENDER_" prefix.
IMAGE_ROOTFS_EXTRA_SPACE="0"
# This factor is multiplied by the used space value for the generated rootfs,
# and if the result is larger than IMAGE_ROOTFS_SIZE + IMAGE_ROOTFS_EXTRA_SPACE,
# it will be used as the size of the rootfs instead of the other two variables.
#
# The actual free space will usually be lower than requested. See comment for
# IMAGE_ROOTFS_EXTRA_SPACE.
#
# This variable directly mirrors the variable from the Yocto Project, which is
# why it is missing a "MENDER_" prefix.
IMAGE_OVERHEAD_FACTOR="1.5"
source configs/mender_grub_config
# Function to create Mender Artifact
#
# This function is defined here, to allow it to be overridden which can be
# achieved by providing this function in a custom configuration file which will
# take precedence of this one.
#
# You might want to override this to e.g provide state-scripts or providing
# a private key to sign your artifact.
mender_create_artifact() {
local -r device_type="${1}"
local -r artifact_name="${2}"
mender_artifact=deploy/${device_type}-${artifact_name}.mender
log_info "Writing Mender artifact to: ${mender_artifact}"
log_info "This can take up to 20 minutes depending on which compression method is used"
run_and_log_cmd "mender-artifact --compression ${MENDER_ARTIFACT_COMPRESSION} \
write rootfs-image \
--file work/rootfs.img \
--output-path ${mender_artifact} \
--artifact-name ${artifact_name} \
--device-type ${device_type}"
}

35
configs/mender_grub_config

@ -0,0 +1,35 @@
# Use U-boot -> GRUB -> EFI boot-loader integration
#
# Only disable this if you know what you are doing
MENDER_GRUB_EFI_INTEGRATION=y
# Specific Linux kernel boot arguments
#
# Typically you would read the content of /proc/cmdline on a "golden image"
# add the appropriate arguments here.
#
# This will override the defaults set by by grub-mender-grubenv, if not an
# empty string
MENDER_GRUB_KERNEL_BOOT_ARGS=""
# grub-mender-grubenv is the Mender integration for the GRUB bootloader
MENDER_GRUBENV_VERSION="1.3.0"
MENDER_GRUBENV_URL="${MENDER_GITHUB_ORG}/grub-mender-grubenv/archive/${MENDER_GRUBENV_VERSION}.tar.gz"
# Name of the storage device containing root filesystem partitions in GRUB
# format.
MENDER_GRUB_STORAGE_DEVICE=hd0
# Type of kernel (bzImage or zImage)
#
# mender-convert will try to determinate this value on its own, only set this
# if was not possible to auto-detect
MENDER_GRUB_KERNEL_IMAGETYPE=""
# Type of initrd image
#
# mender-convert will try to determinate this value on its own, only set this
# if was not possible to auto-detect
MENDER_GRUB_INITRD_IMAGETYPE=""
MENDER_GRUB_BINARY_STORAGE_URL="${MENDER_STORAGE_URL}/mender-convert/grub-efi"

19
configs/qemux86-64_config

@ -0,0 +1,19 @@
# This configuration can be used to run on a qemux86-64 machine.
#
# This has been tested on images generated with the following command:
#
# mkosi -d ubuntu -r bionic -t gpt_ext4 -b --checksum --password password -o image.raw
#
# Converted with the following command:
#
# MENDER_ARTIFACT_NAME=release-1 ./mender-convert --disk-image input/image.raw --overlay rootfs_overlay_demo --config configs/qemux86-64_config
#
# and qemu is executed with the following command:
#
# qemu-system-x86_64 -enable-kvm -m 512 -smp 2 -bios /usr/share/ovmf/x64/OVMF_CODE.fd -drive format=raw,file=qemux86_64-release-1.sdimg
MENDER_STORAGE_DEVICE=/dev/sda
MENDER_DEVICE_TYPE="qemux86_64"
# Nothing to copy
MENDER_COPY_BOOT_GAP="n"

8
configs/raspberrypi0w_config

@ -0,0 +1,8 @@
# Binaries generated with the following script:
#
# https://github.com/drewmoseley/mender-convert-integration-scripts/blob/master/build-uboot-rpi.sh
RASPBERRYPI_BINARIES="raspberrypi0w-integration-2019.01.tar.gz"
RASPBERRYPI_KERNEL_IMAGE="kernel.img"
MENDER_KERNEL_IMAGETYPE=zImage
source configs/raspberrypi_config

8
configs/raspberrypi3_config

@ -0,0 +1,8 @@
# Binaries generated with the following script:
#
# https://github.com/drewmoseley/mender-convert-integration-scripts/blob/master/build-uboot-rpi.shraspberrypi-integration-scripts.tar.gz
RASPBERRYPI_BINARIES="raspberrypi3-integration-2019.01.tar.gz"
RASPBERRYPI_KERNEL_IMAGE="kernel7.img"
MENDER_KERNEL_IMAGETYPE="zImage"
source configs/raspberrypi_config

8
configs/raspberrypi4_config

@ -0,0 +1,8 @@
# Binaries generated with the following script:
#
# https://github.com/drewmoseley/mender-convert-integration-scripts/blob/master/build-uboot-rpi.shraspberrypi-integration-scripts.tar.gz
RASPBERRYPI_BINARIES="raspberrypi4-integration-2019.01.tar.gz"
RASPBERRYPI_KERNEL_IMAGE="kernel7l.img"
MENDER_KERNEL_IMAGETYPE="zImage"
source configs/raspberrypi_config

83
configs/raspberrypi_config

@ -0,0 +1,83 @@
# Raspberry Pi does not support GRUB bootloader integration, fallback to U-boot.
MENDER_GRUB_EFI_INTEGRATION=n
# Nothing to copy
MENDER_COPY_BOOT_GAP=n
# 4MB alignment
MENDER_PARTITION_ALIGNMENT="4194304"
RASPBERRYPI_BINARIES_URL="${MENDER_STORAGE_URL}/mender-convert/raspberrypi/${RASPBERRYPI_BINARIES}"
function platform_modify() {
mkdir -p work/rpi/binaries
run_and_log_cmd "wget -q ${RASPBERRYPI_BINARIES_URL} -P work/rpi/binaries"
run_and_log_cmd "tar xvf work/rpi/binaries/${RASPBERRYPI_BINARIES} -C work/rpi/binaries"
# Make a copy of Linux kernel arguments and modify.
run_and_log_cmd "cp work/boot/cmdline.txt work/rpi/cmdline.txt"
# Set a dynamic rootfs part (required for Mender A/B update strategy)
run_and_log_cmd "sed -i 's/\b[ ]root=[^ ]*/ root=\${mender_kernel_root}/' work/rpi/cmdline.txt"
# Root filesystem can not be resized when the disk is partition according
# to Mender layout, where the rootfs partition is the not last one which
# is a requirement to be able to do an "online" resize.
#
# This disables resize of rootfs on boot but applies the changes to
# cmdline.txt that are performed in the init_resize.sh script.
#
# Extracted from /usr/lib/raspi-config/init_resize.sh
run_and_log_cmd "sed -i 's| init=/usr/lib/raspi-config/init_resize\.sh||' work/rpi/cmdline.txt"
run_and_log_cmd "sed -i 's| sdhci\.debug_quirks2=4||' work/rpi/cmdline.txt"
if ! grep -q splash work/rpi/cmdline.txt; then
run_and_log_cmd "sed -i 's/ quiet//g' work/rpi/cmdline.txt"
fi
# Update Linux kernel command arguments with our custom configuration
run_and_log_cmd "sudo cp work/rpi/cmdline.txt work/boot/"
# Mask udisks2.service, otherwise it will mount the inactive part and we
# might write an update while it is mounted which often result in
# corruptions.
#
# TODO: Find a way to only blacklist mmcblk0pX devices instead of masking
# the service.
run_and_log_cmd "sudo ln -sf /dev/null work/rootfs/etc/systemd/system/udisks2.service"
# Ubuntu Server images actually use U-boot by default on RPi3 and the
# layout is slightly different on the boot partition. That is why we need
# additional logic here to determinant what we are converting.
if [ -e work/boot/uboot.bin ] && [ -e work/boot/vmlinuz ]; then
RASPBERRYPI_KERNEL_IMAGE="vmlinuz"
RASPBERRYPI_BOOTLOADER_IMAGE="uboot.bin"
else
RASPBERRYPI_BOOTLOADER_IMAGE="${RASPBERRYPI_KERNEL_IMAGE}"
fi
# Extract Linux kernel and install to /boot directory on rootfs
run_and_log_cmd "sudo cp work/boot/${RASPBERRYPI_KERNEL_IMAGE} work/rootfs/boot/${MENDER_KERNEL_IMAGETYPE}"
# Replace kernel with U-boot and add boot script
run_and_log_cmd "sudo mkdir -p work/rootfs/uboot"
run_and_log_cmd "sudo cp work/rpi/binaries/u-boot.bin work/boot/${RASPBERRYPI_BOOTLOADER_IMAGE}"
run_and_log_cmd "sudo cp work/rpi/binaries/boot.scr work/boot"
run_and_log_cmd "sudo cp work/rpi/binaries/fw_env.config work/rootfs/etc/"
run_and_log_cmd "sudo cp work/rpi/binaries/uboot-git-log.txt work/boot"
# Raspberry Pi applications expect to find this on the device and in some
# cases parse the options to determinate functionality.
run_and_log_cmd "sudo ln -fs /uboot/config.txt work/rootfs/boot/config.txt"
run_and_log_cmd "sudo ln -fs /uboot/overlays work/rootfs/boot/overlays"
run_and_log_cmd "sudo ln -fs /uboot/cmdline.txt work/rootfs/boot/cmdline.txt"
run_and_log_cmd "sudo install -m 755 work/rpi/binaries/fw_printenv work/rootfs/sbin/fw_printenv"
run_and_log_cmd "sudo ln -fs /sbin/fw_printenv work/rootfs/sbin/fw_setenv"
# Remove original 'resize2fs_once' script and its symbolic link.
if [ -L work/rootfs/etc/rc3.d/S01resize2fs_once ]; then
run_and_log_cmd "sudo unlink work/rootfs/etc/rc3.d/S01resize2fs_once"
fi
run_and_log_cmd "sudo rm -f work/rootfs/etc/init.d/resize2fs_once"
}

28
configs/rockpro64_config

@ -0,0 +1,28 @@
# ROCKPro64 do not support GRUB bootloader integration, fallback to U-boot.
MENDER_GRUB_EFI_INTEGRATION=n
# We will write a modified bootloader
MENDER_COPY_BOOT_GAP=n
ROCKPRO64_BINARIES_URL="${MENDER_STORAGE_URL}/mender-convert/armbian/rockpro64/${ROCKPRO64_BINARIES}"
function platform_modify() {
mkdir -p work/rockpro64
run_and_log_cmd "wget -Nq ${ROCKPRO64_BINARIES_URL} -P work/rockpro64"
run_and_log_cmd "tar xvf work/rockpro64/${ROCKPRO64_BINARIES} -C work/rockpro64"
run_and_log_cmd "sudo cp work/rockpro64/boot.scr work/boot"
run_and_log_cmd "sudo cp work/rockpro64/fw_env.config work/rootfs/etc/"
# It is not possible to resize rootfs part when using Mender. so disable
# the service
run_and_log_cmd "sudo touch work/rootfs/root/.no_rootfs_resize"
}
function platform_package() {
log_info "Embedding bootloader in disk image"
run_and_log_cmd "dd if=work/rockpro64/rksd_loader.img of=${sdimg_path} \
seek=64 conv=notrunc status=none"
}

8
configs/rockpro64_emmc_config

@ -0,0 +1,8 @@
MENDER_STORAGE_DEVICE=/dev/mmcblk1p
# Binaries generated with the following script:
#
# https://d1b0l86ne08fsf.cloudfront.net/mender-convert/armbian/rockpro64/integration-scripts.tar.gz
ROCKPRO64_BINARIES="emmc-boot-integration-2017.09.tar.gz"
source configs/rockpro64_config

6
configs/rockpro64_sd_config

@ -0,0 +1,6 @@
# Binaries generated with the following script:
#
# https://d1b0l86ne08fsf.cloudfront.net/mender-convert/armbian/rockpro64/integration-scripts.tar.gz
ROCKPRO64_BINARIES="sd-boot-integration-2017.09.tar.gz"
source configs/rockpro64_config

22
convert-stage-3.sh

@ -1,22 +0,0 @@
#!/bin/bash
output_dir=$1
rootfs_mapping=$2
build_log=$output_dir/build.log
[ ! -f ${output_dir}/rootfs.img ] && \
{ log "Error: extracted rootfs partition not found. Aborting."; exit 1; }
sudo dd if=${output_dir}/rootfs.img of=/dev/mapper/${rootfs_mapping} bs=8M conv=sparse >> "$build_log" 2>&1
sync
sudo e2fsck -y -f /dev/mapper/${rootfs_mapping} >> "$build_log" 2>&1
sudo resize2fs /dev/mapper/${rootfs_mapping} >> "$build_log" 2>&1
# Check Linux ext4 file system just in case.
sudo fsck.ext4 -fp /dev/mapper/${rootfs_mapping} >> "$build_log" 2>&1
# Make sure the rootfs partition's label follows Mender naming convention.
sudo tune2fs -L "primary" /dev/mapper/${rootfs_mapping} >> "$build_log" 2>&1
log "\tDone."
exit 0

366
convert-stage-4.sh

@ -1,366 +0,0 @@
#!/bin/bash
set -e
show_help() {
cat << EOF
Mender executables, service and configuration files installer.
Usage: $0 [options]
Options: [-m|--mender-disk-image | -g|--mender-client | -a|--artifact-name |
-d|--device-type | -n|--demo | -p|--demo-host-ip | -u| --server-url |
-c|--server-cert -t| --tenant-token -k|--keep -h|--help]
--mender-disk-image - Mender raw disk image
--mender-client - Mender client binary file
--artifact-name - artifact info
--device-type - target device type identification
--demo - Configure image using demo parameters
--demo-host-ip - Mender demo server IP address
--server-url - Mender production server url
--server-cert - Mender server certificate
--tenant-token - Mender tenant token
--keep - Keep intermediate files in output directory
--help - Show help and exit
For examples, see: ./mender-convert --help
EOF
exit 1
}
jq_inplace() {
jq_args="$1"
dest_file="$2"
sudo sh -c -e "jq \"${jq_args}\" ${dest_file} > ${dest_file}.tmp && mv ${dest_file}.tmp ${dest_file}"
}
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
output_dir=${tool_dir}/output
meta_mender_repo="https://raw.githubusercontent.com/mendersoftware/meta-mender"
meta_mender_revision="thud"
mender_dir=$output_dir/mender
device_type=
artifact_name=
# Mender demo server IP address.
demo_host_ip=
# Mender production server url passed as CLI option.
server_url=
# Mender production certificate.
server_cert=
# Mender tenant token passed as CLI option.
tenant_token=
# Mender tenant token.
mender_tenant_token="dummy"
# Mender state-script format version
mender_state_scripts_version="3"
declare -a mender_disk_mappings
append_rootfs_configuration() {
local conffile=$1
local rootfsparta="/dev/mmcblk0p2"
local rootfspartb="/dev/mmcblk0p3"
if [ "$device_type" == "qemux86_64" ]; then
rootfsparta="/dev/hda2"
rootfspartb="/dev/hda3"
elif [ "$device_type" == "rockpro64" ]; then
rootfsparta="/dev/mmcblk1p2"
rootfspartb="/dev/mmcblk1p3"
fi
jq_inplace '.RootfsPartA = \"'$rootfsparta'\" | .RootfsPartB = \"'$rootfspartb'\"' ${conffile}
}
create_client_files() {
cat <<- EOF > $mender_dir/device_type
device_type=${device_type}
EOF
case "$device_type" in
"beaglebone" | "qemux86_64")
cat <<- EOF > $mender_dir/fw_env.config
/dev/mmcblk0 0x800000 0x20000
/dev/mmcblk0 0x1000000 0x20000
EOF
;;
"raspberrypi3"|"raspberrypi0w")
cat <<- EOF > $mender_dir/fw_env.config
/dev/mmcblk0 0x400000 0x4000
/dev/mmcblk0 0x800000 0x4000
EOF
;;
"rockpro64")
cat <<- EOF > $mender_dir/fw_env.config
/dev/mmcblk1 0x400000 0x8000
/dev/mmcblk1 0x800000 0x8000
EOF
;;
esac
}
get_mender_files_from_upstream() {
mkdir -p $mender_dir
log "\tDownloading demo server certificate."
wget -q -O $mender_dir/server.demo.crt \
$meta_mender_repo/$meta_mender_revision/meta-mender-demo/recipes-mender/mender/files/server.crt
}
install_files() {
local primary_dir=$1
local data_dir=$2
sysconfdir="etc/mender"
bindir="usr/bin"
localstatedir="var/lib/mender"
dataconfdir="mender"
databootdir="u-boot"
log "\tInstalling files."
# Prepare 'data' partition
sudo install -d -m 755 ${data_dir}/${dataconfdir}
sudo install -d -m 755 ${data_dir}/${databootdir}
sudo install -d -m 755 ${primary_dir}/${sysconfdir}/scripts/
echo -n "${mender_state_scripts_version}" | sudo tee ${primary_dir}/${sysconfdir}/scripts/version
sudo install -m 0444 ${mender_dir}/device_type ${data_dir}/${dataconfdir}
sudo install -m 0644 ${mender_dir}/fw_env.config ${data_dir}/${databootdir}
sudo ln -sf /data/${databootdir}/fw_env.config ${primary_dir}/etc/fw_env.config
# Create mount-points
#
# Note that only one of /boot/efi or /uboot will be used depending on what
# type of Mender integration is used (GRUB or U-boot). I do not see any
# problems with keeping an empty directory to reduce complexity of creating
# this directory structure.
sudo install -d -m 755 ${primary_dir}/data
sudo install -d -m 755 ${primary_dir}/boot/efi
sudo install -d -m 755 ${primary_dir}/uboot
case "$device_type" in
"qemux86_64")
sudo install -d ${primary_dir}/lib64
sudo ln -sf /lib/ld-linux-x86-64.so.2 ${primary_dir}/lib64/ld-linux-x86-64.so.2
;;
esac
sudo ln -sf /data/${dataconfdir} ${primary_dir}/${localstatedir}
# Call mender make install target
( cd $GOPATH/src/github.com/mendersoftware/mender && \
sudo make install prefix=$primary_dir )
# If specified, replace Mender client binary
if [ -n "${mender_client}" ]; then
sudo install -m 0755 ${mender_client} ${primary_dir}/${bindir}/mender
fi
# Enable menderd service starting on boot.
if [ -z "${standalone_operation}" ]; then
# Enable menderd service starting on boot.
sudo ln -sf /lib/systemd/system/mender.service \
${primary_dir}/etc/systemd/system/multi-user.target.wants/mender.service
fi
# By default production settings configuration is installed
if [ -n "${demo}" ] && [ ${demo} -eq 1 ]; then
sudo install -m 0644 ${primary_dir}/${sysconfdir}/mender.conf.demo ${primary_dir}/${sysconfdir}/mender.conf
fi
# If specified, replace server URL
if [ -n "${server_url}" ]; then
jq_inplace '.ServerURL = \"'${server_url}'\"' ${primary_dir}/${sysconfdir}/mender.conf
fi
# Set tenant token
if [ -n "${tenant_token}" ]; then
jq_inplace '.TenantToken = \"'${tenant_token}'\"' ${primary_dir}/${sysconfdir}/mender.conf
fi
# Append RootfsPartA/B to mender.conf
append_rootfs_configuration ${primary_dir}/${sysconfdir}/mender.conf
# Set artifact name
if [ -n "${artifact_name}" ]; then
sudo sh -c -e "echo artifact_name=${artifact_name} > ${primary_dir}/${sysconfdir}/artifact_info";
fi
# Set demo server and install demo certificate
if [ -n "${demo_host_ip}" ]; then
sudo sh -c -e "echo '$demo_host_ip docker.mender.io s3.docker.mender.io' >> $primary_dir/etc/hosts";
jq_inplace '.ServerURL = \"https://docker.mender.io\"' ${primary_dir}/${sysconfdir}/mender.conf
sudo install -m 0444 ${mender_dir}/server.demo.crt ${primary_dir}/${sysconfdir}/server.crt
fi
# Install provided
if [ -n "${server_cert}" ]; then
sudo install -m 0444 ${server_cert} ${primary_dir}/${sysconfdir}/server.crt
fi
if [ -e "${primary_dir}/${sysconfdir}/server.crt" ]; then
jq_inplace '.ServerCertificate = \"/'${sysconfdir}'/server.crt\"' ${primary_dir}/${sysconfdir}/mender.conf
fi
}
do_install_mender() {
if [ -z "${mender_disk_image}" ]; then
log "Mender raw disk image not set. Aborting."
show_help
fi
if [ -z "${device_type}" ]; then
log "Target device type name not set. Aborting."
show_help
fi
if [ -z "${artifact_name}" ]; then
log "Artifact info not set. Aborting."
show_help
fi
if [ -z "${server_url}" ] && [ -z "${demo_host_ip}" ] && \
[ -z "${tenant_token}" ]; then
log "No Mender server configuration was provided, it will only be possible to update using standalone mode."
standalone_operation="true"
fi
if [ -n "${server_url}" ] && [ -n "${demo_host_ip}" ]; then
log "Incompatible server type choice. Aborting."
show_help
fi
[ ! -f $mender_disk_image ] && \
{ log "$mender_disk_image - file not found. Aborting."; exit 1; }
test -n "$(go version)" || \
{ log "go binary not found in PATH. Aborting."; exit 1; }
test -n "$GOPATH" || \
{ log "GOPATH not set. Aborting."; exit 1; }
test -d $GOPATH/src/github.com/mendersoftware/mender || \
{ log "mender source not found in \$GOPATH/src/github.com/mendersoftware/mender. Aborting."; exit 1; }
# Mount rootfs partition A.
create_device_maps $mender_disk_image mender_disk_mappings
# Change current directory to 'output' directory.
cd $output_dir
primary=${mender_disk_mappings[1]}
data=${mender_disk_mappings[3]}
if [ "$device_type" == "qemux86_64" ]; then
data=${mender_disk_mappings[4]}
fi
map_primary=/dev/mapper/"$primary"
map_data=/dev/mapper/"$data"
path_primary=$output_dir/sdimg/primary
path_data=$output_dir/sdimg/data
mkdir -p ${path_primary} ${path_data}
sudo mount ${map_primary} ${path_primary}
sudo mount ${map_data} ${path_data}
# Get Mender client related files.
get_mender_files_from_upstream
# Create all necessary client's files.
create_client_files
# Create all required paths and install files.
install_files ${path_primary} ${path_data}
# Back to working directory.
cd $tool_dir && sync
# Clean stuff.
detach_device_maps ${mender_disk_mappings[@]}
rm -rf $output_dir/sdimg
[[ $keep -eq 0 ]] && { rm -rf $mender_dir; }
log "\tDone."
}
PARAMS=""
while (( "$#" )); do
case "$1" in
-m | --mender-disk-image)
mender_disk_image=$2
shift 2
;;
-g | --mender-client)
mender_client=$2
shift 2
;;
-d | --device-type)
device_type=$2
shift 2
;;
-a | --artifact-name)
artifact_name=$2
shift 2
;;
-n | --demo)
demo="1"
shift 1
;;
-i | --demo-host-ip)
demo_host_ip=$2
shift 2
;;
-c | --server-cert)
server_cert=$2
shift 2
;;
-u | --server-url)
server_url=$2
shift 2
;;
-t | --tenant-token)
tenant_token=$2
shift 2
;;
-k | --keep)
keep="1"
shift 1
;;
-h | --help)
show_help
;;
--)
shift
break
;;
-*)
log "Error: unsupported option $1"
exit 1
;;
*)
PARAMS="$PARAMS $1"
shift
;;
esac
done
eval set -- "$PARAMS"
# Some commands expect elevated privileges.
sudo true
do_install_mender

389
convert-stage-5.sh

@ -1,389 +0,0 @@
#!/bin/bash
show_help() {
cat << EOF
Tool adding GRUB specific files to Mender compliant image file.
Usage: $0 [options]
Options: [-m|--mender-disk-image | -b|--bootloader-toolchain |
-k|--keep | -d|--device-type]
--mender-disk-image - Mender raw disk image
--bootloader-toolchain - GNU Arm Embedded Toolchain
--device-type - target device type identification
--keep - prevent deleting GRUB workspace
Note: supported device types are: beaglebone, raspberrypi3
Examples:
./mender-convert install-bootloader-to-mender-disk-image
--mender-disk-image <mender_image_path>
--device-type <beaglebone | raspberrypi3>
--bootloader-toolchain arm-linux-gnueabihf
Note: toolchain naming convention is arch-vendor-(os-)abi
arch is for architecture: arm, mips, x86, i686...
vendor is tool chain supplier: apple, Codesourcery, Linux,
os is for operating system: linux, none (bare metal)
abi is for application binary interface convention: eabi, gnueabi, gnueabihf
EOF
}
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
output_dir=${tool_dir}/output
integration_dir=${tool_dir}/integration
grub_dir=$output_dir/grub
grubenv_dir=$output_dir/grubenv
mender_disk_image=
bootloader_toolchain=
device_type=
keep=0
efi_boot=EFI/BOOT
EFI_STUB_VER="4.12.0"
build_log=$output_dir/build.log
declare -a mender_disk_mappings
version() {
echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'
}
get_kernel_version() {
local search_path=$1/boot
local resultvar=$2
[ ! -d "$search_path" ] && { return 1; }
kernel_image=$(ls -1 $search_path | grep -E "^vmlinuz")
local myresult=${kernel_image#*-}
eval $resultvar="'$myresult'"
return 0
}
build_env_lock_boot_files() {
log "\tBuilding boot scripts and tools."
local grubenv_repo_vc_dir=$grubenv_dir/.git
local grubenv_build_dir=$grubenv_dir/build
mkdir -p $grubenv_dir
if [ ! -d $grubenv_repo_vc_dir ]; then
git clone https://github.com/mendersoftware/grub-mender-grubenv.git $grubenv_dir >> "$build_log" 2>&1
fi
cd $grubenv_dir
mkdir -p $grubenv_build_dir
# Remove old defines & settings.
make --quiet distclean >> "$build_log" 2>&1
# Prepare configuration file.
cp mender_grubenv_defines.example mender_grubenv_defines
local kernel_imagetype=kernel
local kernel_devicetree=dtb
sed -i '/^kernel_imagetype/s/=.*$/='${kernel_imagetype}'/' mender_grubenv_defines
sed -i '/^kernel_devicetree/s/=.*$/='${kernel_devicetree//\//\\/}'/' mender_grubenv_defines
if [ "$device_type" == "qemux86_64" ]; then
local root_base=/dev/hda
sed -i '/^mender_kernel_root_base/s/=.*$/='${root_base//\//\\/}'/' mender_grubenv_defines
fi
make --quiet >> "$build_log" 2>&1
rc=$?
[[ $rc -eq 0 ]] && { make --quiet DESTDIR=$grubenv_build_dir install >> "$build_log" 2>&1; }
rc=$?
[[ $rc -ne 0 ]] && { log "\tError: building process failed. Aborting."; }
cd ${output_dir}
return $rc
}
# Takes following arguments:
#
# $1 - linux kernel version
build_grub_efi() {
if [ "$device_type" == "rockpro64" ]; then
return 0
fi
log "\tBuilding GRUB efi file."
local grub_build_dir=$grub_dir/build
local grub_arm_dir=$grub_build_dir/arm
local host=$(uname -m)
local grub_host_dir=$grub_build_dir/$host
local grub_repo_vc_dir=$grub_dir/.git
local version=$(echo $1 | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
# Build grub modules for arm platform and executables for the host.
if [ ! -d $grub_repo_vc_dir ]; then
git clone git://git.savannah.gnu.org/grub.git $grub_dir >> "$build_log" 2>&1
fi
cd $grub_dir
make --quiet distclean >> "$build_log" 2>&1
if [ $(version $version) -lt $(version $EFI_STUB_VER) ]; then
# To avoid error message: "plain image kernel not supported - rebuild
# with CONFIG_(U)EFI_STUB enabled" - use a specific commit.
git checkout 9b37229f0 >> "$build_log" 2>&1
else
git checkout 72e80c025 >> "$build_log" 2>&1
fi
mkdir -p $grub_arm_dir
mkdir -p $grub_host_dir
local cores=$(nproc)
# First build host tools.
./autogen.sh >> "$build_log" 2>&1
./configure --quiet CC=gcc --target=${host} --with-platform=efi --prefix=$grub_host_dir >> "$build_log" 2>&1
make --quiet -j$cores >> "$build_log" 2>&1
make --quiet install >> "$build_log" 2>&1
local format=${host}-efi
grub_name=bootx64.efi
local modules_path=$grub_host_dir/lib/grub/$format/
if [ "$device_type" == "beaglebone" ]; then
# Clean workspace.
make --quiet clean >> "$build_log" 2>&1
make --quiet distclean >> "$build_log" 2>&1
# Now build ARM modules.
./configure --quiet --host=$bootloader_toolchain --with-platform=efi \
--prefix=$grub_arm_dir CFLAGS="-Os -march=armv7-a" \
CCASFLAGS="-march=armv7-a" --disable-werror >> "$build_log" 2>&1
make --quiet -j$cores >> "$build_log" 2>&1
make --quiet install >> "$build_log" 2>&1
format=arm-efi
grub_name=grub-arm.efi
modules_path=$grub_arm_dir/lib/grub/$format/
fi
# Build GRUB EFI image.
${grub_host_dir}/bin/grub-mkimage -v -p /$efi_boot -o $grub_name --format=$format \
-d $modules_path boot linux ext2 fat serial part_msdos part_gpt normal \
efi_gop iso9660 configfile search loadenv test cat echo gcry_sha256 halt \
hashsum loadenv reboot >> "$build_log" 2>&1
rc=$?
[[ $rc -ne 0 ]] && { log "\tBuilding grub.efi failed. Aborting."; } \
|| { log "\tBuilding grub.efi succeeded."; }
cd ${output_dir}
return $rc
}
# Takes following arguments:
#
# $1 - boot partition mountpoint
set_uenv() {
local boot_dir=$1
# Erase/create uEnv.txt file.
sudo install -b -m 644 /dev/null $boot_dir/uEnv.txt
# Fill uEnv.txt file.
cat <<- 'EOF' | sudo tee $boot_dir/uEnv.txt 2>&1 >/dev/null
bootdir=
grubfile=EFI/BOOT/grub-arm.efi
grubaddr=0x80007fc0
loadgrub=fatload mmc 0:1 ${grubaddr} ${grubfile}
grubstart=bootefi ${grubaddr}
uenvcmd=mmc rescan; run loadgrub; run grubstart;
EOF
}
# Takes following arguments:
#
# $1 - boot partition mountpoint
# $2 - primary partition mountpoint
# $3 - linux kernel version
install_files() {
log "\tInstalling GRUB files."
local boot_dir=$1
local rootfs_dir=$2
local linux_version=$3
if [ "$device_type" == "rockpro64" ]; then
cp $integration_dir/rockpro64/boot.scr ${boot_dir}/
# It is not possible to resize rootfs part when using Mender. so disable
# the service
touch ${rootfs_dir}/root/.no_rootfs_resize
return 0
fi
local grub_build_dir=$grub_dir/build
local grub_arm_dir=$grub_build_dir/arm
local grub_host_dir=$grub_build_dir/$(uname -m)
local grubenv_build_dir=$grubenv_dir/build
local grubenv_efi_boot_dir=$grubenv_build_dir/boot/efi/EFI/BOOT/
local efi_boot_dir=$boot_dir/$efi_boot
# Make sure env, lock, lock.sha256sum files exists in working directory.
[[ ! -d $grubenv_efi_boot_dir/mender_grubenv1 || \
! -d $grubenv_efi_boot_dir/mender_grubenv2 ]] && \
{ log "Error: cannot find mender grub related files."; return 1; }
sudo install -d -m 755 $efi_boot_dir
cd $grubenv_efi_boot_dir && find . -type f -exec sudo install -Dm 644 "{}" "$efi_boot_dir/{}" \;
cd ${output_dir}
if [ "$device_type" == "qemux86_64" ]; then
sudo install -m 0755 ${grub_host_dir}/bin/grub-editenv $rootfs_dir/usr/bin
else
sudo install -m 0755 ${grub_arm_dir}/bin/grub-editenv $rootfs_dir/usr/bin
fi
sudo install -m 0644 ${grub_dir}/${grub_name} $efi_boot_dir
sudo install -m 0755 $grubenv_build_dir/usr/bin/fw_printenv $rootfs_dir/sbin/fw_printenv
sudo install -m 0755 $grubenv_build_dir/usr/bin/fw_setenv $rootfs_dir/sbin/fw_setenv
# Replace U-Boot default printenv/setenv commands.
sudo ln -fs /sbin/fw_printenv $rootfs_dir/usr/bin/fw_printenv
sudo ln -fs /sbin/fw_setenv $rootfs_dir/usr/bin/fw_setenv
#Create links for grub
if [ "$device_type" == "qemux86_64" ]; then
# Copy kernel image to fit the grubenv defines.
sudo cp $boot_dir/bzImage $rootfs_dir/boot/kernel
elif [ "$device_type" == "beaglebone" ]; then
#Replace U-Boot default images for Debian 9.5
if [ `grep -s '9.5' $rootfs_dir/etc/debian_version | wc -l` -eq 1 ]; then
sudo cp ${tool_dir}/files/uboot_debian_9.4/MLO ${boot_dir}/MLO
sudo cp ${tool_dir}/files/uboot_debian_9.4/u-boot.img ${boot_dir}/u-boot.img
fi
# Make links to kernel and device tree files.
sudo ln -sf /boot/dtbs/$linux_version/am335x-boneblack.dtb $rootfs_dir/boot/dtb
sudo ln -sf /boot/vmlinuz-$linux_version $rootfs_dir/boot/kernel
set_uenv $boot_dir
fi
}
do_install_bootloader() {
if [ -z "${mender_disk_image}" ]; then
log "Mender raw disk image not set. Aborting."
exit 1
fi
if [ -z "${bootloader_toolchain}" ]; then
log "ARM GCC toolchain not set. Aborting."
exit 1
fi
if [ -z "${device_type}" ]; then
log "Target device type name not set. Aborting."
exit 1
fi
if ! [ -x "$(command -v ${bootloader_toolchain}-gcc)" ]; then
log "Error: ARM GCC not found in PATH. Aborting."
exit 1
fi
[ ! -f $mender_disk_image ] && \
{ log "$mender_disk_image - file not found. Aborting."; exit 1; }
# Map & mount Mender compliant image.
create_device_maps $mender_disk_image mender_disk_mappings
# Change current directory to 'output' directory.
cd $output_dir
boot=${mender_disk_mappings[0]}
primary=${mender_disk_mappings[1]}
map_boot=/dev/mapper/"$boot"
map_primary=/dev/mapper/"$primary"
path_boot=$output_dir/sdimg/boot
path_primary=$output_dir/sdimg/primary
mkdir -p ${path_boot} ${path_primary}
sudo mount ${map_boot} ${path_boot}
sudo mount ${map_primary} ${path_primary}
get_kernel_version ${path_primary} kernel_version
log "\tFound kernel version: $kernel_version"
build_env_lock_boot_files
rc=$?
if [ "$device_type" == "qemux86_64" ]; then
[[ $rc -eq 0 ]] && { build_grub_efi ${EFI_STUB_VER}; }
else
[[ $rc -eq 0 ]] && { build_grub_efi ${kernel_version}; }
fi
rc=$?
[[ $rc -eq 0 ]] && { install_files ${path_boot} ${path_primary} ${kernel_version}; }
rc=$?
# Back to working directory.
cd $tool_dir && sync
detach_device_maps ${mender_disk_mappings[@]}
# Clean files.
rm -rf $output_dir/sdimg
[[ $keep -eq 0 ]] && { rm -rf $grubenv_dir $grub_dir; }
[[ $rc -ne 0 ]] && { exit 1; } || { log "\tDone."; }
}
PARAMS=""
while (( "$#" )); do
case "$1" in
-m | --mender-disk-image)
mender_disk_image=$2
shift 2
;;
-b | --bootloader-toolchain)
bootloader_toolchain=$2
shift 2
;;
-d | --device-type)
device_type=$2
shift 2
;;
-k | --keep)
keep=1
shift 1
;;
-h | --help)
show_help
exit 0
;;
--)
shift
break
;;
-*)
log "Error: unsupported option $1"
exit 1
;;
*)
PARAMS="$PARAMS $1"
shift
;;
esac
done
eval set -- "$PARAMS"
# Some commands expect elevated privileges.
sudo true
do_install_bootloader

1
device-image-shell/.dockerignore

@ -1 +0,0 @@
output/

13
device-image-shell/Dockerfile

@ -1,13 +0,0 @@
FROM ubuntu:18.04
ARG MENDER_ARTIFACT_VERSION=3.2.0
RUN apt-get update && apt-get install -y \
wget \
qemu-user-static
RUN wget -q -O /usr/bin/mender-artifact https://d1b0l86ne08fsf.cloudfront.net/mender-artifact/$MENDER_ARTIFACT_VERSION/mender-artifact \
&& chmod +x /usr/bin/mender-artifact
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

64
device-image-shell/README.md

@ -1,64 +0,0 @@
Device Image Shell
==================
This directory contains a tool that takes the root file system of your device as input and emulates a shell session on your device.
Any commands you run (e.g. apt update, apt upgrade) will behave as if you were logged into your actual device!
The purpose of the tool is to create a new root file system image and Mender Artifact that can be deployed to a fleet of devices in the field.
## Docker environment
To ensure dependencies are correctly set up and it is portable, a docker environment is used.
You need to [install Docker Engine](https://docs.docker.com/install) to use this tool.
### Build the device-image-shell container image
To build a container based on Ubuntu 18.04 with the required dependencies, copy this directory to your workstation and change the current directory to it.
Then run
```bash
./docker-build
```
This will create a container image `device-image-shell`.
### Use the device-image-shell container image
The also assumes your device is based on the ARM architecture, which is the most common (e.g. Raspberry Pi, BeagleBoard, etc.).
You need a root file system image (usually with .ext4 extension) for your device as a starting point, such as one output by [mender-convert](https://github.com/mendersoftware/mender-convert). Make sure to have `qemu-user-static` installed on a host machine.
You can now enter a shell in your device root file system image by running `docker-device-image-shell` with the desired arguments:
1. path to your existing root file system image
2. desired name for the generated Mender Artifact
3. device type, which Mender uses to ensure compatibility between devices and software
For example, if you are using a Raspberry Pi 3, you can run:
```bash
./docker-device-image-shell ../output/2018-11-13-raspbian-stretch-lite.ext4 2018-11-13-raspbian-stretch-lite-aptupgrade raspberrypi3
```
You should now see a shell prompt. You are in an emulated environment, so any commands run here will behave as if you ran them on your device! In addition, any changes you make will be preserved in the output root file system image and Mender Artifact.
For example, to update to the latest packages run:
```bash
apt update
apt upgrade
```
When you are done, press `Ctrl+D` or run the `exit` command. Generating the Mender Artifact will take a few more minutes, depending on the size of the input image and resources available on your workstation.
After it finishes you can find your new `.ext4` and `.mender` files in the `output/` directory. For devices where Mender is installed, you can use the Mender Artifact (`.mender` file) to deploy the changes you made in the shell to all your devices!
### Use caution
Please note that since this tool is using an emulated environment (based on `qemu`) and you are not properly logged in to your device, some things may not work as expected. Look for any relevant errors in commands you run and make sure to test your changes before deploying to production devices!

7
device-image-shell/docker-build

@ -1,7 +0,0 @@
#!/bin/sh
set -e
DOCKER_IMAGE_NAME=device-image-shell
docker build . -t ${DOCKER_IMAGE_NAME}

58
device-image-shell/docker-device-image-shell

@ -1,58 +0,0 @@
#!/bin/sh
set -e
DOCKER_IMAGE_NAME=device-image-shell
OUTPUT_DIR="$(pwd)/output"
show_usage() {
echo "Usage:"
echo "$0 <rootfs input file> <output Artifact name> <device type>"
}
if [ "$#" -ne 3 ]; then
echo "ERROR: 3 parameters required."
show_usage
exit 1
fi
ROOTFS_INPUT_FILE=$1
ARTIFACT_NAME=$2
DEVICE_TYPE=$3
if [ ! -f "$ROOTFS_INPUT_FILE" ]; then
echo "ERROR: File passed as first argument is not accessible."
echo "Got ROOTFS_INPUT_FILE=\"$ROOTFS_INPUT_FILE\""
show_usage
exit 1
fi
mkdir -p $OUTPUT_DIR
ROOTFS_INPUT_FILE_NAME="$(basename -- $ROOTFS_INPUT_FILE)"
ROOTFS_INPUT_FILE_EXTENSION="${ROOTFS_INPUT_FILE_NAME##*.}"
ROOTFS_OUTPUT_FILE_NAME="$ARTIFACT_NAME.$ROOTFS_INPUT_FILE_EXTENSION"
echo "Copying rootfs input file..."
rsync -h --progress $ROOTFS_INPUT_FILE $OUTPUT_DIR/$ROOTFS_OUTPUT_FILE_NAME
docker run \
-ti \
--privileged=true \
--mount type=bind,source=$OUTPUT_DIR,target=/root_images \
$DOCKER_IMAGE_NAME $ROOTFS_OUTPUT_FILE_NAME $ARTIFACT_NAME $DEVICE_TYPE
# Output Artifact gets root owner and group, change to current logged in
CURRENT_USER=$(id -u -n)
CURRENT_GROUP=$(id -g -n)
echo "Changing ownership of Mender Artifact (may ask you to authenticate)"
sudo chown $CURRENT_USER:$CURRENT_GROUP $OUTPUT_DIR/$ARTIFACT_NAME.mender
echo "Image generation complete!"
/bin/echo -e "The new root file system is at:\n\t$OUTPUT_DIR/$ROOTFS_OUTPUT_FILE_NAME"
/bin/echo -e "The new Mender Artifact you can upload to your Mender server to deploy to your devices is at:\
\n\t$OUTPUT_DIR/$ARTIFACT_NAME.mender"

38
device-image-shell/docker-entrypoint.sh

@ -1,38 +0,0 @@
#!/bin/sh
set -e
ROOTFS_OUTPUT_FILE_NAME=$1
ARTIFACT_NAME=$2
DEVICE_TYPE=$3
mkdir /root_system
mount /root_images/$ROOTFS_OUTPUT_FILE_NAME /root_system
if [ -f /root_system/usr/bin/qemu-arm-static ]; then
echo "WARNING: /usr/bin/qemu-arm-static already exists in image. Using this but may be of incompatible version."
QEMU_STATIC_COPIED=false
else
# trick to make chroot into ARM image work
cp /usr/bin/qemu-arm-static /root_system/usr/bin
QEMU_STATIC_COPIED=true
fi
echo "Entering emulated shell in device image. All commands are run as the root user of the device image."
echo "Make changes (e.g. apt update, apt upgrade, wget ...) and press Ctrl-D when done."
# Using bash for command completion support and other conveniences
chroot /root_system /bin/bash
# Mender Artifact name must also be present inside
echo artifact_name=$ARTIFACT_NAME > /root_system/etc/mender/artifact_info
if [ "$QEMU_STATIC_COPIED" = true ]; then
rm /root_system/usr/bin/qemu-arm-static
fi
umount /root_system
echo "Creating Mender Artifact. This may take 10-20 minutes (using LZMA)..."
mender-artifact --compression lzma write rootfs-image -t $DEVICE_TYPE -n $ARTIFACT_NAME -f /root_images/$ROOTFS_OUTPUT_FILE_NAME -o /root_images/$ARTIFACT_NAME.mender
sync

32
docker-build

@ -1,21 +1,21 @@
#!/bin/sh
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
if [ -z "$IMAGE_NAME" ]; then
IMAGE_NAME=mender-convert
fi
IMAGE_NAME=${IMAGE_NAME:-mender-convert}
MENDER_CLIENT_VERSION="2.1.1"
DOCKER_ARGS="--build-arg mender_client_version=${MENDER_CLIENT_VERSION}"
if [ "$1" = "arm64" ]; then
DOCKER_ARGS="${DOCKER_ARGS} --build-arg toolchain_host=aarch64-linux-gnu"
DOCKER_ARGS="${DOCKER_ARGS} --build-arg go_flags=GOARCH=arm64"
else
DOCKER_ARGS="${DOCKER_ARGS} --build-arg toolchain_host=arm-buildroot-linux-gnueabihf"
DOCKER_ARGS="${DOCKER_ARGS} --build-arg go_flags=\"GOARM=6 GOARCH=arm\""
fi
eval docker build . -t ${IMAGE_NAME} ${DOCKER_ARGS}
eval docker build . -t ${IMAGE_NAME} "$@"

17
docker-entrypoint.sh

@ -1,4 +1,18 @@
#!/bin/sh
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
@ -9,3 +23,6 @@ cd /mender-convert
echo "Running mender-convert "$@""
./mender-convert "$@"
# Set owner and group to same as launch directory.
[ -d deploy ] && chown -R --reference=. deploy

20
docker-mender-convert

@ -1,16 +1,30 @@
#!/bin/sh
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
IMAGE_NAME=mender-convert
IMAGE_NAME=${IMAGE_NAME:-mender-convert}
MENDER_CONVERT_DIR="$(pwd)"
mkdir -p output
docker run \
--mount type=bind,source="$MENDER_CONVERT_DIR,target=/mender-convert" \
-v $MENDER_CONVERT_DIR:/mender-convert \
--privileged=true \
--cap-add=SYS_MODULE \
-v /dev:/dev \
-v /lib/modules:/lib/modules:ro \
--env MENDER_ARTIFACT_NAME=${MENDER_ARTIFACT_NAME} \
$IMAGE_NAME "$@"

112
files/init_resize.sh

@ -1,112 +0,0 @@
#!/bin/sh
reboot_pi () {
umount /uboot
sync
echo b > /proc/sysrq-trigger
sleep 5
exit 0
}
check_commands () {
if ! command -v whiptail > /dev/null; then
FAIL_REASON="whiptail not found"
sleep 5
return 1
fi
for COMMAND in grep cut sed parted fdisk; do
if ! command -v $COMMAND > /dev/null; then
FAIL_REASON="$COMMAND not found"
return 1
fi
done
return 0
}
get_variables () {
# /sys/block/mmcblk0/mmcblk0p4/
ROOT_PART_NAME="mmcblk0p4"
ROOT_DEV_NAME="mmcblk0"
ROOT_DEV="/dev/${ROOT_DEV_NAME}"
ROOT_PART_NUM=`cat /sys/block/${ROOT_DEV_NAME}/${ROOT_PART_NAME}/partition`
BOOT_PART_DEV=`cat /proc/mounts | grep " /uboot " | cut -d " " -f 1`
BOOT_PART_NAME=`echo $BOOT_PART_DEV | cut -d "/" -f 3`
BOOT_DEV_NAME=`echo /sys/block/*/${BOOT_PART_NAME} | cut -d "/" -f 4`
BOOT_PART_NUM=`cat /sys/block/${BOOT_DEV_NAME}/${BOOT_PART_NAME}/partition`
ROOT_DEV_SIZE=`cat /sys/block/${ROOT_DEV_NAME}/size`
TARGET_END=`expr $ROOT_DEV_SIZE - 1`
PARTITION_TABLE=`parted -m $ROOT_DEV unit s print | tr -d 's'`
LAST_PART_NUM=`echo "$PARTITION_TABLE" | tail -n 1 | cut -d ":" -f 1`
ROOT_PART_LINE=`echo "$PARTITION_TABLE" | grep -e "^${ROOT_PART_NUM}:"`
ROOT_PART_START=`echo $ROOT_PART_LINE | cut -d ":" -f 2`
ROOT_PART_END=`echo $ROOT_PART_LINE | cut -d ":" -f 3`
}
check_variables () {
if [ $ROOT_PART_NUM -ne $LAST_PART_NUM ]; then
FAIL_REASON="Data partition should be last partition"
return 1
fi
if [ $ROOT_PART_END -gt $TARGET_END ]; then
FAIL_REASON="Data partition runs past the end of device"
return 1
fi
if [ ! -b $ROOT_DEV ] || [ ! -b $ROOT_PART_DEV ] || [ ! -b $BOOT_PART_DEV ] ; then
FAIL_REASON="Could not determine partitions"
return 1
fi
}
main () {
get_variables
if ! check_variables; then
return 1
fi
if [ $ROOT_PART_END -eq $TARGET_END ]; then
reboot_pi
fi
if ! parted -m $ROOT_DEV u s resizepart $ROOT_PART_NUM $TARGET_END; then
FAIL_REASON="Data partition resize failed"
return 1
fi
return 0
}
mount -t proc proc /proc
mount -t sysfs sys /sys
mount /uboot
sed -i 's| init=/usr/lib/raspi-config/init_resize.sh||' /uboot/cmdline.txt
sed -i 's| sdhci\.debug_quirks2=4||' ${output_dir}/cmdline.txt
if ! grep -q splash /uboot/cmdline.txt; then
sed -i "s/ quiet//g" /uboot/cmdline.txt
fi
mount /uboot -o remount,ro
sync
echo 1 > /proc/sys/kernel/sysrq
if ! check_commands; then
reboot_pi
fi
if main; then
whiptail --infobox "Resized data partition. Rebooting in 5 seconds..." 20 60
sleep 5
else
sleep 5
whiptail --msgbox "Could not expand filesystem, please try raspi-config.\n${FAIL_REASON}" 20 60
fi
reboot_pi

13
files/resizefs.service

@ -1,13 +0,0 @@
[Unit]
Description=Expand data partition file system
After=mender.service
[Service]
Type=simple
User=root
Group=root
ExecStart=/bin/sh -c 'sleep 1 ; /usr/sbin/resizefs.sh start'
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

42
files/resizefs.sh

@ -1,42 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: resizefs
# Required-Start:
# Required-Stop:
# Default-Start: 3
# Default-Stop:
# Short-Description: Resize the data filesystem to fill partition
# Description:
### END INIT INFO
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting resizefs service (once)"
DISK=$(lsblk -f | sed -n '2{p;q}')
DISK_DEV="/dev/$DISK"
DISK_SIZE=$(blockdev --getsize $DISK_DEV)
DATA_PART_DEV=$(findmnt /data -o source -n)
DATA_PART=$(basename $DATA_PART_DEV)
DATA_PART_START=$(parted -m $DISK_DEV unit s print | tr -d 's' | tail -n 1 | cut -d ":" -f 2)
DATA_PART_SIZE=$(blockdev --getsize $DATA_PART_DEV)
FREE_SPACE=`expr $DISK_SIZE - $DATA_PART_START - $DATA_PART_SIZE`
if [ $FREE_SPACE -eq 0 ]; then
log_daemon_msg "Data partition already resized, aborting"
else
resize2fs $DATA_PART_DEV
FSSIZEMEG=`expr $FREE_SPACE + $DATA_PART_SIZE`
FSSIZEMEG=`expr $FSSIZEMEG / 2 / 1024`"M"
log_daemon_msg "Resizing $DATA_PART finished, new size is $FSSIZEMEG"
fi
systemctl --no-reload disable resizefs.service
log_end_msg $?
;;
*)
echo "Usage: $0 start" >&2
exit 3
;;
esac

BIN
files/uboot_debian_9.4/MLO

Binary file not shown.

BIN
files/uboot_debian_9.4/u-boot.img

Binary file not shown.

31
files/variables.template

@ -1,31 +0,0 @@
MENDER_BOOT_PART="/dev/mmcblk0p1"
MENDER_BOOT_PART_DEFAULT="/dev/mmcblk0p1"
MENDER_BOOT_PART_FSTYPE="auto"
MENDER_BOOT_PART_FSTYPE_DEFAULT="auto"
MENDER_BOOT_PART_MOUNT_LOCATION=""
MENDER_BOOT_PART_SIZE_MB="16"
MENDER_BOOT_PART_SIZE_MB_DEFAULT="16"
MENDER_DATA_PART_DEFAULT="/dev/mmcblk0p4"
MENDER_DATA_PART_FSTYPE="auto"
MENDER_DATA_PART_FSTYPE_DEFAULT="auto"
MENDER_DATA_PART_SIZE_MB="128"
MENDER_DATA_PART_SIZE_MB_DEFAULT="128"
MENDER_DEVICE_TYPE="vexpress-qemu"
MENDER_DEVICE_TYPES_COMPATIBLE="vexpress-qemu"
MENDER_DEVICE_TYPES_COMPATIBLE_DEFAULT="vexpress-qemu"
MENDER_DEVICE_TYPE_DEFAULT="vexpress-qemu"
MENDER_PARTITIONING_OVERHEAD_KB="32768"
MENDER_PARTITION_ALIGNMENT=""
MENDER_ROOTFS_PART_A="/dev/mmcblk0p2"
MENDER_ROOTFS_PART_B="/dev/mmcblk0p3"
MENDER_STORAGE_DEVICE="/dev/mmcblk0"
MENDER_STORAGE_TOTAL_SIZE_MB=""
MENDER_UBOOT_ENV_STORAGE_DEVICE_OFFSET=""
MENDER_CALC_ROOTFS_SIZE=""
MENDER_ARTIFACT_NAME="release-1"
MENDER_MACHINE="vexpress-qemu"
HOST_ARCH="arm"
IMAGE_FSTYPES=" tar.bz2 ext4 mender mender.bmap sdimg sdimg.bmap"
ARTIFACTIMG_FSTYPE="ext4"
DISTRO_FEATURES=""
DEPLOY_DIR_IMAGE=""

893
mender-convert

@ -1,9 +1,24 @@
#!/bin/bash
show_help() {
#! /usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
MENDER_CONVERT_VERSION=$(git describe --tags --dirty --exact-match 2>/dev/null || git rev-parse --short HEAD)
function show_help() {
cat << EOF
Mender conversion tool
mender-convert
A tool that takes an existing embedded image (Debian, Ubuntu, Raspbian, etc)
and converts it to a Mender image by restructuring partition table and adding
@ -11,860 +26,74 @@ necessary files.
Usage: $0 COMMAND [options]
General commands:
from-raw-disk-image - composes fully functional Mender
image compliant with Mender
partition layout, having all
necessary files installed
mender-disk-image-to-artifact - creates Mender artifact file
from Mender image
Options: [-r|--raw-disk-image | -m|--mender-disk-image | -s|--data-part-size-mb |
-d|--device-type | -p|--rootfs-partition-id | -n|--demo | -i|--demo-host-ip |
-c|--server-cert | -u|--server-url | -t|--tenant-token |
-g|--mender-client | -b|--bootloader-toolchain | -a|--artifact-name |
-e|--storage-total-size-mb | -k|--keep | -v|--version -h|--help]
raw-disk-image - raw disk embedded Linux (Debian, Raspbian,
Ubuntu, etc.) image path
mender-disk-image - Mender disk image name where the script writes to
should have "sdimg" suffix
data-part-size-mb - data partition size in MB; default value 128MB
device-type - target device identification used to build
Mender image
rootfs-partition-id - selects root filesystem (primary|secondary)
as the source filesystem for an artifact
demo - Configure image using demo parameters
demo-host-ip - server demo ip used for testing purposes
server-cert - server certificate file
server-url - production server url
tenant-token - Mender tenant token
mender-client - Mender client binary
bootloader-toolchain - GNU Arm Embedded Toolchain
artifact-name - Mender artifact name
storage-total-size-mb - total storage size in MB; default value 8000MB;
it is used to calculate the rootfs final size
keep - keep intermediate files in output directory
version - print the version
help - show help and exit
Note: root filesystem size used in Mender image creation can be found as
an output from 'raw-disk-image-shrink-rootfs' command or, in case
of using unmodified embedded raw disk image, can be checked with
any partition manipulation program (e.g. parted, fdisk, etc.).
Examples:
To create fully functional Mender image from raw disk image in a single step:
./mender-convert from-raw-disk-image
--raw-disk-image <raw_disk_image_path>
[--mender-disk-image <mender_image_name>]
--device-type <beaglebone | raspberrypi3>
[--mender-client <mender_binary_path>]
--artifact-name release-1_1.5.0
--bootloader-toolchain arm-linux-gnueabihf
--demo-host-ip 192.168.10.2
--keep
Output:
- Mender image: ready to use image with client and bootloader installed
- Mender artifact: update file based on the already built Mender image
- Mender root filesystem: EXT4 image used to produce the Mender artifact
To create Mender artifact file from Mender image:
./mender-convert mender-disk-image-to-artifact
--mender-disk-image <mender_image_path>
--device-type <beaglebone | raspberrypi3>
--artifact-name release-1_1.5.0
--rootfs-partition-id <primary | secondary>
Note: artifact name format is: release-<release_no>_<mender_version>
Options:
-d, --disk-image - Path to disk image to convert (Debian, Raspbian,
Ubuntu, etc)
-o, --overlay - Path to root filesystem overlay directory, you
are able to provide this option multiple times
-c, --config - Path to configuration file, you are able to
provide this option multiple times
-v, --version - output version information and exit
-h, --help - display this help and exit
EOF
}
show_version() {
local version=$(cd "$tool_dir" && (git describe --tags --dirty --exact-match 2>/dev/null || git rev-parse --short HEAD))
echo "mender-convert version: $version"
}
if [ $# -eq 0 ]; then
show_help
exit 1
fi
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
mender_disk_image=
raw_disk_image=
device_type=
mender_disk_counts=
artifact_name=
rootfs_partition_id=
raw_disk_counts=
mender_client=
# Mender production certificate.
server_cert=
# Mender production server url.
server_url=
# Mender demo server IP address.
demo_host_ip=
# Mender hosted token.
tenant_token=
# Conversion progress.
declare -i step=1
declare -i total=
declare -a rootfs_partition_ids=("primary" "secondary")
declare -a mender_disk_mappings
declare -a raw_disk_mappings
#Supported devices
declare -a supported_devices=("beaglebone" "raspberrypi3" "qemux86_64" "raspberrypi0w" "rockpro64")
do_raw_disk_image_shrink_rootfs() {
log "$step/$total Shrinking raw disk image root filesystem..."
((step++))
if [ -z "${raw_disk_image}" ]; then
log "Raw disk image not set. Aborting."
return 1
fi
# For root filesystem partition set 8MB alignment for shrinking purpose.
partition_alignment=$PART_ALIGN_8MB
# Gather information about raw disk image.
get_raw_disk_sizes ${raw_disk_image} raw_disk_counts raw_disk_sizes
local sector_size=${raw_disk_sizes[sector_size]}
if [[ $raw_disk_counts -eq 1 ]]; then
offset=${raw_disk_sizes[pboot_start]}
elif [[ $raw_disk_counts -eq 2 ]]; then
offset=${raw_disk_sizes[prootfs_start]}
else
log "Warning: invalid/unsupported embedded raw disk image. Skipping resize..."
return 0
fi
# Find first available loopback device and mount appropriate partition.
loopdevice=$(sudo losetup --show -f -o $((${offset} * $sector_size)) $raw_disk_image)
[ $? -ne 0 ] && { log "Error: inaccesible loopback device"; return 1; }
block_size=($(sudo dumpe2fs -h $loopdevice | grep 'Block size' | tr -s ' ' | cut -d ' ' -f3))
min_size_blocks=($(sudo resize2fs -P $loopdevice | awk '{print $NF}'))
new_size_sectors=$(( $min_size_blocks * $block_size / $sector_size ))
align_partition_size new_size_sectors $sector_size
log "\tRoot filesystem size:\
\n\tminimal: $(( $min_size_blocks * $block_size )) \
\n\taligned: $(( $new_size_sectors * $sector_size ))\
\n\tsectors: $new_size_sectors"
sudo e2fsck -y -f $loopdevice >> "$build_log" 2>&1
sudo resize2fs -p $loopdevice ${new_size_sectors}s >> "$build_log" 2>&1
sudo e2fsck -y -f $loopdevice >> "$build_log" 2>&1
sudo losetup -d $loopdevice
loopdevice=$(sudo losetup --show -f $raw_disk_image)
if [[ $raw_disk_counts -eq 1 ]]; then
create_single_disk_partition_table $loopdevice \
${raw_disk_sizes[pboot_start]} $new_size_sectors
elif [[ $raw_disk_counts -eq 2 ]]; then
create_double_disk_partition_table $loopdevice \
${raw_disk_sizes[prootfs_start]} $new_size_sectors
fi
sudo partprobe
endsector=($(sudo parted $loopdevice -ms unit s print | grep "^$raw_disk_counts" | cut -f3 -d: | sed 's/[^0-9]*//g'))
sudo losetup -d $loopdevice
log "\tRaw disk image new endsector: $endsector"
sudo truncate -s $((($endsector+1) * $sector_size)) $raw_disk_image
log "\tNew root filesystem size (sectors): $new_size_sectors"
return 0
}
do_raw_disk_image_create_partitions() {
log "$step/$total Repartitioning raw disk image..."
((step++))
if [ -z "$raw_disk_image" ] || [ -z "$device_type" ] || \
[ -z "$artifact_name" ]; then
show_help
return 1
fi
if [[ ! -f ${raw_disk_image} ]]; then
log "Raw disk image not found. Aborting."
return 1
fi
local supported=$(echo ${supported_devices[@]} | grep -o $device_type | wc -w)
[[ $supported -eq 0 ]] && \
{ log "Error: incorrect device type. Aborting."; return 1; }
# Change current directory to 'output' directory.
cd $output_dir
set_mender_disk_alignment $device_type partition_alignment vfat_storage_offset
get_raw_disk_sizes ${raw_disk_image} raw_disk_counts raw_disk_sizes
rc=$?
[ $rc -eq 0 ] || { return 1; }
log "\tDetected raw disk image with $raw_disk_counts partition(s)."
log "\tCalculating partitions' sizes of the Mender image."
set_mender_disk_sizes ${raw_disk_counts} ${partition_alignment} \
${vfat_storage_offset} ${data_part_size_mb} \
raw_disk_sizes mender_disk_sizes
local mender_disk_image_size=
calculate_mender_disk_size ${partition_alignment} mender_disk_sizes \
mender_disk_counts mender_disk_image_size
mender_image_size_to_total_storage_size ${mender_disk_counts} \
mender_disk_image_size ${storage_total_size_mb} \
mender_disk_sizes
rc=$?
[ $rc -eq 0 ] || { return 1; }
if [[ ${raw_disk_counts} -gt 1 ]]; then
log "\tExtracting boot partition from raw disk image."
extract_file_from_image ${raw_disk_image} ${raw_disk_sizes[pboot_start]} \
${raw_disk_sizes[pboot_size]} "boot.vfat"
log "\tExtracting root filesystem partition from raw disk image."
extract_file_from_image ${raw_disk_image} ${raw_disk_sizes[prootfs_start]} \
${raw_disk_sizes[prootfs_size]} "rootfs.img"
else
log "\tGenerating boot partition (required, does not exist in original image)"
dd if=/dev/zero of=${output_dir}/boot.vfat count=${mender_disk_sizes[pboot_size]} bs=512 >> "$build_log" 2>&1
mkfs.vfat ${output_dir}/boot.vfat >> "$build_log" 2>&1
log "\tExtracting root filesystem partition from raw disk image."
extract_file_from_image ${raw_disk_image} ${raw_disk_sizes[pboot_start]} \
${raw_disk_sizes[pboot_size]} "rootfs.img"
fi
local sector_size=${mender_disk_sizes[sector_size]}
log "\tCreating blank Mender disk image:\
\n\t\timage size: ${mender_disk_image_size} bytes\
\n\t\tboot partition size: $((${mender_disk_sizes[pboot_size]} * $sector_size)) bytes\
\n\t\troot filesystem size: $((${mender_disk_sizes[prootfs_size]} * $sector_size)) bytes\
\n\t\tdata partition size: $((${mender_disk_sizes[pdata_size]} * $sector_size)) bytes"
if [ -v mender_disk_sizes[pswap_size] ]; then
log "\t\tswap partition size: $((${mender_disk_sizes[pswap_size]} * $sector_size)) bytes"
fi
create_test_config_file $device_type $partition_alignment $mender_disk_image_size \
mender_disk_sizes
create_mender_disk $mender_disk_image $mender_disk_image_size
format_mender_disk $mender_disk_image $mender_disk_image_size \
$partition_alignment mender_disk_sizes
rc=$?
[ $rc -eq 0 ] || { return 1; }
verify_mender_disk $mender_disk_image $mender_disk_counts
rc=$?
[ $rc -eq 0 ] || { return 1; }
create_device_maps $mender_disk_image mender_disk_mappings
log "$step/$total Formatting repartitioned raw disk image..."
((step++))
make_mender_disk_filesystem ${mender_disk_mappings[@]}
case "$device_type" in
"beaglebone")
do_make_sdimg_beaglebone
;;
"raspberrypi3" | "raspberrypi0w")
do_make_sdimg_raspberrypi3
;;
"qemux86_64")
do_make_sdimg_qemux86_64
;;
"rockpro64")
do_make_sdimg_rockpro64
;;
*)
log "Error: unsupported device type $device_type"
exit 1
;;
esac
rc=$?
log "$step/$total Cleaning intermediate files..."
((step++))
# Clean and detach.
detach_device_maps ${mender_disk_mappings[@]}
detach_device_maps ${raw_disk_mappings[@]}
sync
rm -rf $embedded_base_dir
rm -rf $sdimg_base_dir
[ $rc -eq 0 ] || { log "\nRepartitioning of $raw_disk_image image failed."; }
return $rc
}
do_make_sdimg_rockpro64() {
local ret=0
create_device_maps $raw_disk_image raw_disk_mappings
mount_raw_disk ${raw_disk_mappings[@]}
log "$step/$total Setting boot partition..."
((step++))
stage_2_args="$output_dir ${mender_disk_mappings[0]} ${embedded_rootfs_dir}"
${tool_dir}/rockpro64-convert-stage-2.sh ${stage_2_args} || ret=$?
[[ $ret -ne 0 ]] && { log "Aborting."; return $ret; }
log "$step/$total Setting root filesystem partition..."
((step++))
stage_3_args="$output_dir ${mender_disk_mappings[1]}"
${tool_dir}/convert-stage-3.sh ${stage_3_args} || ret=$?
[[ $ret -ne 0 ]] && { return $ret; }
mount_mender_disk ${mender_disk_mappings[@]}
log "$step/$total Setting file system table..."
((step++))
set_fstab $device_type
log "Write bootloader"
dd if=$integration_dir/rockpro64/rksd_loader.img of=$mender_disk_image \
seek=64 conv=notrunc status=none
return $ret
}
do_make_sdimg_beaglebone() {
local ret=0
create_device_maps $raw_disk_image raw_disk_mappings
mount_raw_disk ${raw_disk_mappings[@]}
log "$step/$total Setting boot partition..."
((step++))
stage_2_args="$output_dir ${mender_disk_mappings[0]} ${embedded_rootfs_dir}"
${tool_dir}/bbb-convert-stage-2.sh ${stage_2_args} || ret=$?
[[ $ret -ne 0 ]] && { log "Aborting."; return $ret; }
log "$step/$total Setting root filesystem partition..."
((step++))
stage_3_args="$output_dir ${mender_disk_mappings[1]}"
${tool_dir}/convert-stage-3.sh ${stage_3_args} || ret=$?
[[ $ret -ne 0 ]] && { return $ret; }
mount_mender_disk ${mender_disk_mappings[@]}
log "$step/$total Setting file system table..."
((step++))
set_fstab $device_type
return $ret
}
do_make_sdimg_raspberrypi3() {
log "$step/$total Setting boot partition..."
((step++))
stage_2_args="$output_dir ${mender_disk_mappings[0]}"
${tool_dir}/rpi-convert-stage-2.sh ${stage_2_args} || ret=$?
[[ $ret -ne 0 ]] && { return $ret; }
log "$step/$total Setting root filesystem partition..."
((step++))
stage_3_args="$output_dir ${mender_disk_mappings[1]}"
${tool_dir}/convert-stage-3.sh ${stage_3_args} || ret=$?
[[ $ret -ne 0 ]] && { return $ret; }
mount_mender_disk ${mender_disk_mappings[@]}
log "$step/$total Setting file system table..."
((step++))
set_fstab $device_type
return 0
}
do_make_sdimg_qemux86_64() {
log "$step/$total Setting boot partition..."
((step++))
stage_2_args="$output_dir ${mender_disk_mappings[0]}"
${tool_dir}/qemux86_64-convert-stage-2.sh ${stage_2_args} || ret=$?
[[ $ret -ne 0 ]] && { return $ret; }
log "$step/$total Setting root filesystem partition..."
((step++))
stage_3_args="$output_dir ${mender_disk_mappings[1]}"
${tool_dir}/convert-stage-3.sh ${stage_3_args} || ret=$?
[[ $ret -ne 0 ]] && { return $ret; }
mount_mender_disk ${mender_disk_mappings[@]}
log "$step/$total Setting file system table..."
((step++))
set_fstab $device_type
return 0
}
do_install_mender_to_mender_disk_image() {
log "$step/$total Installing Mender to Mender disk image..."
((step++))
if [ -z "$mender_disk_image" ] || [ -z "$device_type" ] || \
[ -z "$artifact_name" ]; then
show_help
return 1
fi
local supported=$(echo ${supported_devices[@]} | grep -o $device_type | wc -w)
[[ $supported -eq 0 ]] && \
{ log "Error: incorrect device type. Aborting."; return 1; }
# mender-image-1.5.0
stage_4_args="-m $mender_disk_image -d $device_type -a ${artifact_name}"
if [ -n "$mender_client" ]; then
stage_4_args="${stage_4_args} --mender-client ${mender_client}"
fi
if [ -n "$demo" ] && [ ${demo} -eq 1 ]; then
stage_4_args="${stage_4_args} --demo"
fi
if [ -n "$demo_host_ip" ]; then
stage_4_args="${stage_4_args} -i ${demo_host_ip}"
fi
if [ -n "$server_cert" ]; then
stage_4_args="${stage_4_args} -c ${server_cert}"
fi
if [ -n "$server_url" ]; then
stage_4_args="${stage_4_args} -u ${server_url}"
fi
if [ -n "${tenant_token}" ]; then
stage_4_args="${stage_4_args} -t ${tenant_token}"
fi
if [ -n "${keep}" ]; then
stage_4_args="${stage_4_args} -k"
fi
eval set -- " ${stage_4_args}"
${tool_dir}/convert-stage-4.sh ${stage_4_args} || ret=$?
[[ $ret -ne 0 ]] && { log "\nInstalling Mender to Mender disk image failed."; return $ret; }
# Update test configuration file
update_test_config_file $device_type artifact-name $artifact_name
return 0
}
do_install_bootloader_to_mender_disk_image() {
log "$step/$total Installing Bootloader to Mender disk image..."
((step++))
if [ -z "$mender_disk_image" ] || [ -z "$device_type" ] || \
[ -z "$bootloader_toolchain" ]; then
show_help
return 1
fi
local supported=$(echo ${supported_devices[@]} | grep -o $device_type | wc -w)
[[ $supported -eq 0 ]] && \
{ log "Error: incorrect device type. Aborting."; return 1; }
case "$device_type" in
"beaglebone" | "qemux86_64" | "rockpro64")
stage_5_args="-m $mender_disk_image -d $device_type -b ${bootloader_toolchain} $keep"
eval set -- " ${stage_5_args}"
${tool_dir}/convert-stage-5.sh ${stage_5_args}|| ret=$?
[[ $ret -ne 0 ]] && { log "\nInstalling Bootloader failed."; return $ret; }
# Update test configuration file
update_test_config_file $device_type distro-feature "mender-grub" \
mount-location "\/boot\/efi"
;;
"raspberrypi3"|"raspberrypi0w")
stage_5_args="-m $mender_disk_image -d $device_type -b ${bootloader_toolchain} $keep"
eval set -- " ${stage_5_args}"
${tool_dir}/rpi-convert-stage-5.sh ${stage_5_args}|| ret=$?
[[ $ret -ne 0 ]] && { log "\nInstalling Bootloader failed."; return $ret; }
# Update test configuration file
update_test_config_file $device_type distro-feature "mender-uboot" \
mount-location "\/uboot"
;;
*)
log "Error: unsupported device type $device_type"
exit 1
;;
esac
return 0
function show_version() {
echo "Version: ${MENDER_CONVERT_VERSION}"
}
do_mender_disk_image_to_artifact() {
log "$step/$total Creating Mender Artifact..."
((step++))
if [ -z "${mender_disk_image}" ]; then
log "Mender disk image not set. Aborting."
return 1
fi
if [ -z "${device_type}" ]; then
log "Target device_type name not set. Aborting."
return 1
fi
if [ -z "${artifact_name}" ]; then
log "Artifact name not set. Aborting."
return 1
fi
if [ -z "${rootfs_partition_id}" ]; then
log "\tRootfs partition id not set - 'primary' will be used by default."
rootfs_partition_id="primary"
fi
local supported=$(echo ${supported_devices[@]} | grep -o $device_type | wc -w)
[[ $supported -eq 0 ]] && \
{ log "Error: incorrect device type. Aborting."; return 1; }
inarray=$(echo ${rootfs_partition_ids[@]} | grep -o $rootfs_partition_id | wc -w)
[[ $inarray -eq 0 ]] && \
{ log "Error: invalid rootfs partition id provided. Aborting."; return 1; }
local count=0
local sector_size=0
local prootfs_start=0
local prootfs_size=0
local rootfs_a_start=0
local rootfs_a_size=0
local rootfs_b_start=0
local rootfs_b_size=0
local rootfs_path=
local mender_device_type=
get_mender_disk_sizes $mender_disk_image count sector_size rootfs_a_start \
rootfs_a_size rootfs_b_start rootfs_b_size
ret=$?
[[ $ret -ne 0 ]] && \
{ log "Error: cannot validate Mender disk image. Aborting."; return 1; }
# Check if device type matches.
create_device_maps $mender_disk_image mender_disk_mappings
mount_mender_disk ${mender_disk_mappings[@]}
# Find .sdimg file's dedicated device type.
mender_device_type=$( cat $sdimg_data_dir/mender/device_type | sed 's/[^=].*=//' )
if [ "$mender_device_type" != "$device_type" ]; then
log "Error: device types of Mender artifact & Mender not matching. Aborting."
ret=1
fi
if ! [ -x "$(command -v mender-artifact)" ]; then
log "Error: mender-artifact not found in PATH. Aborting."
ret=1
fi
if [ $ret -eq 0 ]; then
if [[ $rootfs_partition_id == "primary" ]]; then
prootfs_start=$rootfs_a_start
prootfs_size=$rootfs_a_size
rootfs_path=$sdimg_primary_dir
elif [[ $rootfs_partition_id == "secondary" ]]; then
prootfs_start=$rootfs_b_start
prootfs_size=$rootfs_b_size
rootfs_path=$sdimg_secondary_dir
fi
# Extract root filesystem ext4 image to use it to generate Mender artifact.
# Ext4 disk image will be also verified in acceptance tests.
extract_file_from_image $mender_disk_image $prootfs_start \
$prootfs_size $mender_rootfs_basename
fsck.ext4 -fp $mender_rootfs_image &> /dev/null || ret=$?
[[ $ret -ne 0 ]] && \
{ log "Error: checking $mender_rootfs_basename file system failed. Aborting."; }
# Find first available loopback device and use it.
loopdevice=($(sudo losetup --show -f ${mender_rootfs_image} || ret=$?))
[[ $ret -ne 0 ]] && \
{ log "Error: cannot find an unused loop device. Aborting."; }
if [ $ret -eq 0 ]; then
# Mount extracted ext4 partition to verify 'artifact_info' file content.
rootfs_mountpoint=${output_dir}/mnt/${rootfs_partition_id}
mkdir -p ${rootfs_mountpoint}
sudo mount $loopdevice ${rootfs_mountpoint}
# Set 'artifact name' as passed in the command line.
sudo sed -i '/^artifact/s/=.*$/='${artifact_name}'/' "${rootfs_mountpoint}/etc/mender/artifact_info"
sudo umount -l ${rootfs_mountpoint}
sudo losetup -d $loopdevice
rm -rf ${output_dir}/mnt
log "\tWriting Mender artifact to: ${mender_artifact}"
log "\tThis may take 10-20 minutes (using LZMA)..."
#Create Mender artifact
mender-artifact \
--compression lzma \
write rootfs-image \
--file ${mender_rootfs_image} \
--output-path ${mender_artifact} \
--artifact-name ${artifact_name} \
--device-type ${device_type}
ret=$?
[[ $ret -eq 0 ]] && \
{ log "\tCreating Mender Artifact succeeded."; } || \
{ log "\tCreating Mender Artifact failed."; }
fi
fi
# Clean and detach.
detach_device_maps ${mender_disk_mappings[@]}
rm -rf $sdimg_base_dir
return $ret
function trap_exit() {
sudo rm -rf work
}
do_from_raw_disk_image() {
if [ -z "$raw_disk_image" ] || [ -z "$device_type" ] || \
[ -z "$artifact_name" ] || \
[ -z "$bootloader_toolchain" ]; then
show_help
return 1
fi
do_raw_disk_image_shrink_rootfs || rc=$?
[[ $rc -ne 0 ]] && { return 1; }
do_raw_disk_image_create_partitions || rc=$?
[[ $rc -ne 0 ]] && { return 1; }
do_install_mender_to_mender_disk_image || rc=$?
[[ $rc -ne 0 ]] && { return 1; }
do_install_bootloader_to_mender_disk_image || rc=$?
[[ $rc -ne 0 ]] && { return 1; }
do_mender_disk_image_to_artifact || rc=$?
[[ $rc -ne 0 ]] && { return 1; }
return 0
function trap_term() {
echo "Program interrupted by user"
}
PARAMS=""
# Load necessary functions.
source ${tool_dir}/mender-convert-functions.sh
export -f create_device_maps
export -f detach_device_maps
export -f mount_mender_disk
export -f log
export -f logsetup
trap trap_term INT TERM
trap trap_exit EXIT
# We only handle a selection of the arguments here, the rest are passed on
# to the sub-scripts.
while (("$#")); do
case "$1" in
-p | --rootfs-partition-id)
rootfs_partition_id=$2
shift 2
;;
-m | --mender-disk-image)
mender_disk_image=$2
shift 2
;;
-r | --raw-disk-image)
raw_disk_image=$(get_path $2)
shift 2
;;
-s | --data-part-size-mb)
data_part_size_mb=$2
shift 2
;;
-d | --device-type)
device_type=$2
shift 2
;;
-a | --artifact-name)
artifact_name=$2
shift 2
;;
-g | --mender-client)
mender_client=$(get_path $2)
shift 2
;;
-b | --bootloader-toolchain)
bootloader_toolchain=$2
shift 2
;;
-n | --demo)
demo="1"
shift 1
;;
-i | --demo-host-ip)
demo_host_ip=$2
shift 2
;;
-c | --server-cert)
server_cert=$(get_path $2)
shift 2
;;
-u | --server-url)
server_url=$2
shift 2
;;
-t | --tenant-token)
tenant_token=$2
shift 2
;;
-e | --storage-total-size-mb)
storage_total_size_mb=$2
shift 2
;;
-k | --keep)
keep="-k"
shift 1
;;
-v | --version)
show_version
exit 0
;;
-h | --help)
show_help
exit 0
;;
--)
shift
break
;;
-*)
log "Error: unsupported option $1"
exit 1
-v | --version)
show_version
exit 0
;;
*)
PARAMS="$PARAMS $1"
shift
break
;;
esac
done
[ -z "${data_part_size_mb}" ] && \
{ log "*** Data partition size set to default value: 128MB ***"; \
data_part_size_mb=$DATA_PART_SIZE_MB; }
[ -z "${storage_total_size_mb}" ] && \
{ log "*** Total storage size set to default value: 8GB ***"; \
storage_total_size_mb=$STORAGE_TOTAL_SIZE_MB; }
eval set -- "$PARAMS"
# Before running any command first create output directory
# and configure where logs should be redirected.
mkdir -p $output_dir
logsetup
# Fetch integration binaries
MENDER_INTEGRATION_URL="https://d1b0l86ne08fsf.cloudfront.net/mender-convert"
if [ "$device_type" == "rockpro64" ]; then
if [ ! -f $integration_dir/rockpro64/emmc-boot-integration.tar.gz ]; then
rockpro64_integration_dir=$integration_dir/rockpro64
mkdir -p $rockpro64_integration_dir
wget -P $rockpro64_integration_dir \
${MENDER_INTEGRATION_URL}/armbian/rockpro64/emmc-boot-integration.tar.gz
tar xvf $rockpro64_integration_dir/emmc-boot-integration.tar.gz -C $rockpro64_integration_dir
fi
mender_convert_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
if [ "${mender_convert_dir}" != "${PWD}" ]; then
echo "You must execute mender-convert from the root directory: ${mender_convert_dir}"
exit 1
fi
# Some commands expect elevated privileges.
sudo true
# Make sure the user's given Mender image name has a correct extension.
# If Mender image name is not provided, then use following syntax:
# mender-<device_name>-<artifact_name>.sdimg
if [ -n "${mender_disk_image}" ]; then
mender_disk_filename=$(basename -- "$mender_disk_image")
if [[ $mender_disk_filename =~ \.sdimg$ ]]; then
mender_disk_image=$output_dir/${mender_disk_filename}
else
mender_disk_image=$output_dir/${mender_disk_filename}.sdimg
fi
else
mender_disk_filename="mender-${device_type}-${artifact_name}"
mender_disk_image=$output_dir/${mender_disk_filename}.sdimg
if [ -z "${MENDER_ARTIFACT_NAME}" ]; then
echo "Sorry, it seems that you have not defined MENDER_ARTIFACT_NAME"
echo "You can do this with the following command:"
echo ""
echo -e "\tMENDER_ARTIFACT_NAME=\"release-1\" ./mender-convert"
exit 1
fi
mender_disk_basename="${mender_disk_filename%.*}"
mender_rootfs_basename=${mender_disk_basename}.ext4
mender_rootfs_image=${output_dir}/${mender_rootfs_basename}
mender_artifact=${output_dir}/${mender_disk_basename}.mender
source modules/bootstrap.sh
case "$1" in
mender-disk-image-to-artifact)
total=1
do_mender_disk_image_to_artifact || rc=$?
[[ $rc -ne 0 ]] && { log "Check $build_log for details."; exit 1; }
log "A Mender Artifact has been successfully extracted from your Mender disk image!"
log "You can find the Mender Artifact at:\n\t$mender_artifact\nand use it to deploy updates."
;;
from-raw-disk-image)
total=10
do_from_raw_disk_image || rc=$?
[[ $rc -ne 0 ]] && { log "Check $build_log for details."; exit 1; }
log "Conversion complete!"
log "The Mender disk image you can provision your device storage with is at:\
\n\t${mender_disk_image}"
log "The Mender root file system partition is at:\n\t${mender_rootfs_image}"
log "The Mender Artifact you can upload to your Mender server to deploy to your devices is at:\
\n\t${mender_artifact}"
;;
*)
show_help
;;
esac
mkdir -p work
touch work/convert.log
[[ $keep != "-k" ]] && { rm -f $output_dir/boot.vfat \
$output_dir/cmdline.txt $output_dir/config.txt \
$output_dir/syslinux.cfg $output_dir/rootfs.img; }
./mender-convert-extract "$@"
./mender-convert-modify "$@"
./mender-convert-package "$@"
exit 0
echo "Output Artifacts and images can be found in the deploy directory:"
ls -1 deploy/*

109
mender-convert-extract

@ -0,0 +1,109 @@
#! /usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
echo "Running $(basename $0): $@"
source modules/bootstrap.sh
source modules/disk.sh
# The mender_convert_config is always used and provides all the defaults
declare -a configs=("configs/mender_convert_config")
disk_image=""
while (( "$#" )); do
case "$1" in
-o | --overlay)
overlays+="${2}"
shift 2
;;
-c | --config)
configs+=("${2}")
shift 2
;;
-d | --disk-image)
disk_image="${2}"
shift 2
;;
*)
log_fatal "Sorry but the provided option is not supported: $1"
;;
esac
done
if [ -z "${disk_image}" ]; then
log_warn "Sorry, but '--disk-image' is a mandatory option"
log_warn "See ./mender-convert --help for more information"
exit 1
fi
if [ ! -e ${disk_image} ]; then
log_fatal "File not found: ${disk_image}"
fi
# Note the use of %q formatting here. This is a bash feature to add
# proper quoting to the strings so that spaces and special characters
# will be treated properly. Primarily for supporting spaces in
# pathnames and avoid splitting those into multiple parameters.
source modules/config.sh $(printf "%q " "${configs[@]}")
MKFS_VFAT="/usr/bin/mkfs.vfat"
if [ ! -f ${MKFS_VFAT} ]; then
MKFS_VFAT="/sbin/mkfs.vfat"
fi
declare -i nr_of_parts=$(disk_get_nr_of_parts ${disk_image})
log_info "Validating disk image"
if [ ${nr_of_parts} -eq 0 ]; then
log_fatal "Sorry, but could not find any valid partitions for: ${disk_image}"
fi
log_info "Disk parsed successfully"
log_info "NUMBER OF PARTS: ${nr_of_parts} TYPE: $(disk_get_part_value ${disk_image} 1 SCHEME)"
for ((n=1;n<=${nr_of_parts};n++)); do
part_dst_file="work/part-${n}.fs"
if [ "$(disk_get_part_value ${disk_image} ${n} TYPE)" == "0x8e" ]; then
log_fatal "Detected an LVM volume group on disk. Unfortunately this is not yet supported"
fi
log_info "PART ${n}: SIZE: $(disk_get_part_value ${disk_image} ${n} SIZE) TYPE: $(disk_get_part_value ${disk_image} ${n} TYPE)"
log_info "PART ${n}: extracting to ${part_dst_file}"
disk_extract_part "${disk_image}" $(disk_get_part_value ${disk_image} ${n} START) \
$(disk_get_part_value ${disk_image} ${n} SECTORS) ${part_dst_file}
done
# Some disk images will not have an boot partition for us to extract,
# but we do require it be present and hence we might need to generate
# a filesystem image here.
if [ ${nr_of_parts} -eq 1 ]; then
log_info "Generating boot partition (required, does not exist in original image)"
run_and_log_cmd "dd if=/dev/zero of=work/boot-generated.vfat count=${MENDER_BOOT_PART_SIZE_MB} bs=1M status=none"
run_and_log_cmd "${MKFS_VFAT} work/boot-generated.vfat"
fi
# Extract boot gap, that is the area from sector 1 until first part, and
# this is done because some images might contain the bootloader embedded here
# and we might need to keep this area intact when we generate our custom
# image.
if [ "${MENDER_COPY_BOOT_GAP}" == "y" ]; then
log_info "Extracting boot gap to work/boot-gap.bin"
disk_extract_part "${disk_image}" \
1 $(( $(disk_get_part_value ${disk_image} 1 START) - 1)) work/boot-gap.bin
fi

888
mender-convert-functions.sh

@ -1,888 +0,0 @@
#!/bin/bash
# Partition alignment value in bytes.
declare -i partition_alignment
# Boot partition storage offset in bytes.
declare -i vfat_storage_offset
PART_ALIGN_4MB=4194304
PART_ALIGN_8MB=8388608
# Default 'data' partition size in MiB.
declare -i data_part_size_mb
# Default total storage size in MiB.
declare -i storage_total_size_mb
DATA_PART_SIZE_MB=128
STORAGE_TOTAL_SIZE_MB=8000
# Number of required heads in a final image.
declare -i -r heads=255
# Number of required sectors in a final image.
declare -i -r sectors=63
declare -a mender_partitions_regular=('boot' 'primary' 'secondary' 'data')
declare -a mender_partitions_extended=('boot' 'primary' 'secondary' 'n/a' 'data' 'swap')
declare -a raw_disk_partitions=('boot' 'rootfs')
declare -A raw_disk_sizes
declare -A mender_disk_sizes
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
files_dir=${tool_dir}/files
output_dir=${tool_dir}/output
integration_dir=${tool_dir}/integration
build_log=${output_dir}/build.log
embedded_base_dir=$output_dir/embedded
sdimg_base_dir=$output_dir/sdimg
embedded_boot_dir=$embedded_base_dir/boot
embedded_rootfs_dir=$embedded_base_dir/rootfs
sdimg_boot_dir=$sdimg_base_dir/boot
sdimg_primary_dir=$sdimg_base_dir/primary
sdimg_secondary_dir=$sdimg_base_dir/secondary
sdimg_data_dir=$sdimg_base_dir/data
logsetup() {
[ ! -f $build_log ] && { touch $build_log; }
echo -n "" > $build_log
exec > >(tee -a $build_log)
exec 2>&1
}
log() {
echo -e "$*"
}
# Takes following arguments:
#
# $1 -relative file path
get_path() {
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
}
get_part_number_from_device() {
case "$1" in
/dev/*[0-9]p[1-9])
echo ${1##*[0-9]p}
;;
/dev/[sh]d[a-z][1-9])
echo ${1##*d[a-z]}
;;
ubi[0-9]_[0-9])
echo ${1##*[0-9]_}
;;
[a-z]*\.sdimg[1-9])
echo ${1##*\.sdimg}
;;
/dev/mapper/*[0-9]p[1-9])
echo ${1##*[0-9]p}
;;
*)
log "Could not determine partition number from $1"
exit 1
;;
esac
}
# Takes following arguments:
#
# $1 - raw disk image
# $2 - boot partition start offset (in sectors)
# $3 - boot partition size (in sectors)
create_single_disk_partition_table() {
local device=$1
local bootstart=$2
local stopsector=$(( $3 - 1 ))
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | sudo fdisk $device &> /dev/null
d # delete partition
n # new partition
p # primary partition
1 # partion number 1
${bootstart}
+${stopsector}
a # set boot flag
w # write the partition table
q # and we're done
EOF
}
# Takes following arguments:
#
# $1 - raw disk image
# $2 - root filesystem partition start offset (in sectors)
# $3 - root filesystem partition size (in sectors)
create_double_disk_partition_table() {
local device=$1
local rootfsstart=$2
local rootfsstop=$(( $3 - 1 ))
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | sudo fdisk $device &> /dev/null
d # delete partition
2
n
p
2
${rootfsstart}
+${rootfsstop}
w # write the partition table
q # and we're done
EOF
}
# Takes following arguments:
#
# $1 - raw_disk image path
#
# Calculates following values:
#
# $2 - number of partitions
# $3 - array of partitions' sizes for raw disk
#
get_raw_disk_sizes() {
local limage=$1
local rvar_count=$2
shift 2
local rvar_array=($@)
local lsubname=${limage:0:10}
local lfdisk="$(fdisk -u -l ${limage})"
local lparts=($(echo "${lfdisk}" | grep "^${lsubname}" | cut -d' ' -f1))
local lcount=${#lparts[@]}
if [[ $lcount -gt 3 ]]; then
log "\tError: invalid/unsupported raw disk image. Aborting."
return 1
fi
local lsectorsize=($(echo "${lfdisk}" | grep '^Sector' | cut -d' ' -f4))
local idx_start=2
local idx_size=4
local lfirstpartinfo="$(echo "${lfdisk}" | grep "^${lparts[0]}")"
if [[ $lcount -gt 1 ]]; then
local lsecondpartinfo="$(echo "${lfdisk}" | grep "^${lparts[1]}")"
eval $rvar_array[prootfs_start]="'$(echo "${lsecondpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_start})'"
eval $rvar_array[prootfs_size]="'$(echo "${lsecondpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_size})'"
fi
if [[ $lcount -gt 2 ]]; then
local lthirdpartinfo="$(echo "${lfdisk}" | grep "^${lparts[2]}")"
eval $rvar_array[pswap_start]="'$(echo "${lthirdpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_start})'"
eval $rvar_array[pswap_size]="'$(echo "${lthirdpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_size})'"
fi
# Check is first partition is marked as bootable.
if [[ "$lfirstpartinfo" =~ .*\*.* ]]; then
((idx_start+=1))
((idx_size+=1))
fi
eval $rvar_array[pboot_start]="'$(echo "${lfirstpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_start})'"
eval $rvar_array[pboot_size]="'$(echo "${lfirstpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_size})'"
eval $rvar_array[sector_size]="'$lsectorsize'"
eval $rvar_count="'$lcount'"
return 0
}
# Takes the following argument
# $1 - device type
#
# Calculates the following arguments:
# $2 - partition alignment
# $3 - vfat storage offset
set_mender_disk_alignment() {
local rvar_partition_alignment=$2
local rvar_vfat_storage_offset=$3
case "$1" in
"beaglebone" | "qemux86_64")
local lvar_partition_alignment=${PART_ALIGN_8MB}
local lvar_vfat_storage_offset=$lvar_partition_alignment
;;
"raspberrypi3" | "raspberrypi0w" | "rockpro64")
local lvar_partition_alignment=${PART_ALIGN_4MB}
local lvar_uboot_env_size=$(( $lvar_partition_alignment * 2 ))
local lvar_vfat_storage_offset=$(( $lvar_partition_alignment + $lvar_uboot_env_size ))
;;
*)
log "Error: unsupported device type $1"
exit 1
;;
esac
eval $rvar_partition_alignment="'$lvar_partition_alignment'"
eval $rvar_vfat_storage_offset="'$lvar_vfat_storage_offset'"
}
# Takes following arguments:
#
# $1 - Mender disk image path
#
# Calculates following values:
#
# $2 - number of partitions
# $3 - size of the sector (in bytes)
# $4 - rootfs A partition start offset (in sectors)
# $5 - rootfs A partition size (in sectors)
# $6 - rootfs B partition start offset (in sectors)
# $7 - rootfs B partition size (in sectors)
get_mender_disk_sizes() {
local limage=$1
local rvar_count=$2
local rvar_sectorsize=$3
local rvar_rootfs_a_start=$4
local rvar_rootfs_a_size=$5
local rvar_rootfs_b_start=$6
local rvar_rootfs_b_size=$7
local lsubname=${limage:0:10}
local lfdisk="$(fdisk -u -l ${limage})"
local lparts=($(echo "${lfdisk}" | grep "^${lsubname}" | cut -d' ' -f1))
local lcount=${#lparts[@]}
if [[ $lcount -ne 4 ]] && [[ $lcount -ne 6 ]]; then
log "Error: invalid Mender disk image. Aborting."
return 1
else
local lsectorsize=($(echo "${lfdisk}" | grep '^Sector' | cut -d' ' -f4))
local lrootfs_a_info="$(echo "${lfdisk}" | grep "^${lparts[1]}")"
local lrootfs_b_info="$(echo "${lfdisk}" | grep "^${lparts[2]}")"
idx_start=2
idx_size=4
local lrootfs_a_start=($(echo "${lrootfs_a_info}" | tr -s ' ' | cut -d' ' -f${idx_start}))
local lrootfs_a_size=($(echo "${lrootfs_a_info}" | tr -s ' ' | cut -d' ' -f${idx_size}))
local lrootfs_b_start=($(echo "${lrootfs_b_info}" | tr -s ' ' | cut -d' ' -f${idx_start}))
local lrootfs_b_size=($(echo "${lrootfs_b_info}" | tr -s ' ' | cut -d' ' -f${idx_size}))
eval $rvar_count="'$lcount'"
eval $rvar_sectorsize="'$lsectorsize'"
eval $rvar_rootfs_a_start="'$lrootfs_a_start'"
eval $rvar_rootfs_a_size="'$lrootfs_a_size'"
eval $rvar_rootfs_b_start="'$lrootfs_b_start'"
eval $rvar_rootfs_b_size="'$lrootfs_b_size'"
return 0
fi
}
# Takes following arguments:
#
# $1 - size variable to be aligned in sectors
# $2 - size of the sector
#
align_partition_size() {
# Final size is aligned with reference to 'partition_alignment' variable.
local rvar_size=$1
local -n ref=$1
local size_in_bytes=$(( $ref * $2 ))
local reminder=$(( ${size_in_bytes} % ${partition_alignment} ))
if [ $reminder -ne 0 ]; then
size_in_bytes=$(( $size_in_bytes - $reminder + ${partition_alignment} ))
fi
local lsize=$(( $size_in_bytes / $2 ))
eval $rvar_size="'$lsize'"
}
# Takes following arguments:
#
# $1 - number of partition of the raw disk image
# $2 - mender image partition alignment
# $3 - mender image's boot partition offset
# $4 - data partition size (in MB)
# $5 - array of partitions' sizes for raw image
#
# Returns:
#
# $6 - array of partitions' sizes for Mender image
#
set_mender_disk_sizes() {
local count=$1
local alignment=$2
local offset=$3
local datasize_mb=$4
local _raw_sizes=$(declare -p $5)
eval "declare -A raw_sizes="${_raw_sizes#*=}
shift 5
local rvar_array=($@)
local sector_size=${raw_sizes[sector_size]}
local datasize=$(( ($datasize_mb * 1024 * 1024) / $sector_size ))
local bootstart=
local bootsize=
local rootfsstart=
local rootfssize=
local bootflag=
if [[ $count -eq 1 ]]; then
# Default size of the boot partition: 16MiB.
bootsize=$(( (${alignment} * 2) / ${sector_size} ))
# Root filesystem size is determined by the size of the single partition.
rootfssize=${raw_sizes[pboot_size]}
else
bootsize=${raw_sizes[pboot_size]}
rootfssize=${raw_sizes[prootfs_size]}
fi
# Boot partition storage offset is defined from the top down.
bootstart=$(( ${offset} / ${sector_size} ))
align_partition_size bootsize $sector_size
align_partition_size rootfssize $sector_size
align_partition_size datasize $sector_size
eval $rvar_array[pboot_start]="'$bootstart'"
eval $rvar_array[pboot_size]="'$bootsize'"
eval $rvar_array[prootfs_size]="'$rootfssize'"
eval $rvar_array[pdata_size]="'$datasize'"
if [[ $count -eq 3 ]]; then
# Add space for Swap partition.
local swapsize=${raw_sizes[pswap_size]}
align_partition_size swapsize $sector_size
eval $rvar_array[pswap_size]="'$swapsize'"
fi
eval $rvar_array[sector_size]="'$sector_size'"
}
# Takes following arguments:
#
# $1 - partition alignment
# $2 - array of partitions' sizes for Mender image
#
# Returns:
#
# #3 - number of partitions of the Mender disk image
# $4 - final Mender disk image size (in bytes)
#
calculate_mender_disk_size() {
local _mender_sizes=$(declare -p $2)
eval "declare -A mender_sizes="${_mender_sizes#*=}
local sector_size=${mender_sizes[sector_size]}
local rvar_counts=$3
local rvar_sdimgsize=$4
local sdimgsize=$(( (${mender_sizes[pboot_start]} + ${mender_sizes[pboot_size]} + \
2 * ${mender_sizes[prootfs_size]} + \
${mender_sizes[pdata_size]}) * $sector_size ))
eval $rvar_counts="'4'"
if [ -v mender_sizes[pswap_size] ]; then
log "\tSwap partition found."
# Add size of the swap partition to the total size.
sdimgsize=$(( $sdimgsize + (${mender_sizes[pswap_size]} * $sector_size) ))
# Add alignment used as swap partition offset.
sdimgsize=$(( $sdimgsize + 2 * ${1} ))
eval $rvar_counts="'6'"
fi
eval $rvar_sdimgsize="'$sdimgsize'"
}
# Takes following arguments:
#
# $1 - number of partition of the Mender image
# $2 - calculated total size of the Mender image in bytes
# $3 - expected total size of the Mender image in mb
# $4 - array of partitions' sizes for Mender image
#
# Returns:
#
# Size of the rootfs partition updated and adjusted to total storage size
#
mender_image_size_to_total_storage_size() {
local count=$1
local rvar_image_size=$2
local -n image_size_bytes=$2
local storage_size_mb=$3
local _mender_sizes=$(declare -p $4)
eval "declare -A mender_sizes="${_mender_sizes#*=}
shift 3
local rvar_array=($@)
local image_size_mb=$(( (($image_size_bytes / 1024) / 1024) ))
log "\tAdjust Mender disk image size to the total storage size (${storage_size_mb}MB)."
if [ $image_size_mb -gt $storage_size_mb ]; then
log "\tDefined total storage size of ${3}MB is too small."
log "\tMinimal required storage is ${image_size_mb}MB. Aborting."
return 1
elif [ $image_size_mb -eq $storage_size_mb ]; then
# Simply continue.
log "\tCalculated Mender image size exactly fits the defined total storage."
return 0
fi
# Get spare space for rootfs a/b partitions (in sectors).
local sector_size=${mender_sizes[sector_size]}
local image_size_s=$(( $image_size_bytes / $sector_size ))
local storage_size_bytes=$(( ($storage_size_mb * 1024 * 1024) ))
local storage_size_s=$(( $storage_size_bytes / $sector_size ))
local rootfs_overplus_bytes=0
local spare_storage_bytes=$(( $storage_size_bytes - $image_size_bytes ))
if [ $(($spare_storage_bytes % 2)) -ne 0 ]; then
log "\tAdditional space for rootfs partitions not divisible by 2.\
\n\tFinal image will be smaller than ${storage_size_mb}MB"
fi
rootfs_overplus_bytes=$(( $spare_storage_bytes / 2 ))
local reminder=$(( ${rootfs_overplus_bytes} % ${partition_alignment} ))
if [ $reminder -ne 0 ]; then
log "\tAdditional space for rootfs partitions not aligned.\
\n\tFinal image will be smaller than ${storage_size_mb}MB"
fi
rootfs_overplus_bytes=$(($rootfs_overplus_bytes - $reminder))
rootfs_overplus_s=$(( $rootfs_overplus_bytes / $sector_size ))
local prootfs_size=${mender_sizes[prootfs_size]}
prootfs_size=$(( $prootfs_size + $rootfs_overplus_s ))
image_size_bytes=$(( $image_size_bytes + 2 * $rootfs_overplus_bytes ))
eval $rvar_array[prootfs_size]="'$prootfs_size'"
eval $rvar_image_size="'$image_size_bytes'"
return 0
}
# Takes following arguments:
#
# $1 - raw disk image path
# $2 - raw disk image size
create_mender_disk() {
local lfile=$1
local lsize=$2
# Generates a sparse image
dd if=/dev/zero of=${lfile} seek=${lsize} bs=1 count=0 >> "$build_log" 2>&1
}
# Takes following arguments:
#
# $1 - Mender disk image path
# $2 - Mender disk image size
# $3 - partition alignment
# $4 - array of partitions' sizes for Mender image
#
format_mender_disk() {
local lfile=$1
local lsize=$2
local rc=0
local _mender_sizes=$(declare -p $4)
eval "declare -A mender_sizes="${_mender_sizes#*=}
local sector_size=${mender_sizes[sector_size]}
local alignment=$(($3 / ${sector_size}))
cylinders=$(( ${lsize} / ${heads} / ${sectors} / ${sector_size} ))
pboot_start=${mender_sizes[pboot_start]}
pboot_size=${mender_sizes[pboot_size]}
pboot_end=$((${pboot_start} + ${pboot_size} - 1))
prootfs_size=${mender_sizes[prootfs_size]}
prootfsa_start=$((${pboot_end} + 1))
prootfsa_end=$((${prootfsa_start} + ${prootfs_size} - 1))
prootfsb_start=$((${prootfsa_end} + 1))
prootfsb_end=$((${prootfsb_start} + ${prootfs_size} - 1))
pdata_start=$((${prootfsb_end} + 1))
pdata_size=${mender_sizes[pdata_size]}
pdata_end=$((${pdata_start} + ${pdata_size} - 1))
if [ -v mender_sizes[pswap_size] ]; then
local pextended_start=$((${prootfsb_end} + 1))
pdata_start=$(($pextended_start + ${alignment}))
pdata_end=$((${pdata_start} + ${pdata_size} - 1))
local pswap_start=$((${pdata_end} + ${alignment} + 1))
local pswap_size=${mender_sizes[pswap_size]}
local pswap_end=$((${pswap_start} + ${pswap_size} - 1))
fi
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | sudo fdisk ${lfile} &> /dev/null
o # clear the in memory partition table
x
h
${heads}
s
${sectors}
c
${cylinders}
r
w # write the partition table
q # and we're done
EOF
# Create partition table
sudo parted -s ${lfile} mklabel msdos || rc=$?
sudo parted -s ${lfile} unit s mkpart primary fat32 ${pboot_start} ${pboot_end} || rc=$?
sudo parted -s ${lfile} set 1 boot on || rc=$?
sudo parted -s ${lfile} -- unit s mkpart primary ext4 ${prootfsa_start} ${prootfsa_end} || rc=$?
sudo parted -s ${lfile} -- unit s mkpart primary ext4 ${prootfsb_start} ${prootfsb_end} || rc=$?
if [ -v mender_sizes[pswap_size] ]; then
log "\tAdding swap partition."
sudo parted -s ${lfile} -- unit s mkpart extended ${pextended_start} 100% || rc=$?
sudo parted -s ${lfile} -- unit s mkpart logical ext4 ${pdata_start} ${pdata_end} || rc=$?
sudo parted -s ${lfile} -- unit s mkpart logical linux-swap ${pswap_start} ${pswap_end} || rc=$?
else
sudo parted -s ${lfile} -- unit s mkpart primary ext4 ${pdata_start} ${pdata_end} || rc=$?
fi
[[ $rc -eq 0 ]] && { log "\tChanges in partition table applied."; }
return $rc
}
# Takes following arguments:
#
# $1 - raw disk file
#
verify_mender_disk() {
local lfile=$1
local lcounts=$2
local limage=$(basename $lfile)
local partitions=($(fdisk -l -u ${limage} | cut -d' ' -f1 | grep 'sdimg[1-9]\{1\}$'))
local no_of_parts=${#partitions[@]}
[[ $no_of_parts -eq $lcounts ]] || \
{ log "Error: incorrect number of partitions: $no_of_parts. Aborting."; return 1; }
return 0
}
# Takes following arguments:
#
# $1 - raw disk image
# $2 - partition mappings holder
create_device_maps() {
local -n mappings=$2
if [[ -n "$1" ]]; then
loopdevice=$(sudo losetup --show -f $1)
mapfile -t mappings < <( sudo kpartx -s -v -a $loopdevice | grep 'loop' | cut -d' ' -f3 )
[[ ${#mappings[@]} -eq 0 ]] \
&& { log "Error: partition mappings failed. Aborting."; exit 1; }
else
log "Error: no device passed. Aborting."
exit 1
fi
sudo partprobe /dev/${mappings[0]%p*}
}
# Takes following arguments:
#
# $1 - partition mappings holder
detach_device_maps() {
local mappings=($@)
[ ${#mappings[@]} -eq 0 ] && { log "\tPartition mappings cleaned."; return; }
local mapper=${mappings[0]%p*}
for mapping in ${mappings[@]}
do
map_dev=/dev/mapper/"$mapping"
is_mounted=`grep ${map_dev} /proc/self/mounts | wc -l`
if [ ${is_mounted} -ne 0 ]; then
sudo umount -l $map_dev
fi
done
sudo kpartx -d /dev/$mapper &
sudo losetup -d /dev/$mapper &
wait && sync
}
# Takes following arguments:
#
# $1 - partition mappings holder
#
make_mender_disk_filesystem() {
local mappings=($@)
local counts=${#mappings[@]}
if [ $counts -eq 4 ]; then
local labels=(${mender_partitions_regular[@]})
elif [ $counts -eq 6 ]; then
local labels=(${mender_partitions_extended[@]})
fi
for mapping in ${mappings[@]}
do
map_dev=/dev/mapper/"$mapping"
part_no=$(get_part_number_from_device $map_dev)
label=${labels[${part_no} - 1]}
case ${part_no} in
1)
log "\tCreating MS-DOS filesystem for '$label' partition."
sudo mkfs.vfat -n ${label} $map_dev >> "$build_log" 2>&1
;;
2|3)
log "\tCreating ext4 filesystem for '$label' partition."
sudo mkfs.ext4 -L ${label} $map_dev >> "$build_log" 2>&1
;;
4)
if [ $counts -eq 4 ]; then
log "\tCreating ext4 filesystem for '$label' partition."
sudo mkfs.ext4 -L ${label} $map_dev >> "$build_log" 2>&1
else
continue
fi
;;
5)
log "\tCreating ext4 filesystem for '$label' partition."
sudo mkfs.ext4 -L ${label} $map_dev >> "$build_log" 2>&1
;;
6)
log "\tCreating swap area for '$label' partition."
sudo mkswap -L ${label} $map_dev >> "$build_log" 2>&1
;;
*)
break
;;
esac
done
}
# Takes following arguments:
#
# $1 - partition mappings holder
mount_raw_disk() {
local mappings=($@)
if [ ${#mappings[@]} -eq 1 ]; then
local path=$embedded_rootfs_dir
mkdir -p $path
sudo mount /dev/mapper/"${mappings[0]}" $path
return
fi
for mapping in ${mappings[@]}
do
local part_no=${mapping#*p*p}
local path=$embedded_base_dir/${raw_disk_partitions[${part_no} - 1]}
mkdir -p $path
sudo mount /dev/mapper/"${mapping}" $path 2>&1 >/dev/null
done
}
# Takes following arguments:
#
# $1 - partition mappings holder
mount_mender_disk() {
local mappings=($@)
local counts=${#mappings[@]}
if [ $counts -eq 4 ]; then
local labels=(${mender_partitions_regular[@]})
elif [ $counts -eq 6 ]; then
local labels=(${mender_partitions_extended[@]})
fi
for mapping in ${mappings[@]}
do
local part_no=${mapping#*p*p}
local path=$sdimg_base_dir/${labels[${part_no} - 1]}
mkdir -p $path
case ${part_no} in
1|2|3)
sudo mount /dev/mapper/"${mapping}" $path 2>&1 >/dev/null
;;
4)
if [ $counts -eq 4 ]; then
sudo mount /dev/mapper/"${mapping}" $path 2>&1 >/dev/null
else
# Skip extended partition.
continue
fi
;;
5)
sudo mount /dev/mapper/"${mapping}" $path 2>&1 >/dev/null
;;
6)
# Skip swap partition.
continue
;;
*)
break
;;
esac
done
}
# Takes following arguments
#
# $1 - device type
set_fstab() {
local mountpoint=
local blk_device=
local data_id=4
local device_type=$1
local sysconfdir="$sdimg_primary_dir/etc"
[ ! -d "${sysconfdir}" ] && { log "Error: cannot find rootfs config dir."; exit 1; }
# Erase/create the fstab file.
sudo install -b -m 644 /dev/null ${sysconfdir}/fstab
case "$device_type" in
"beaglebone")
mountpoint="/boot/efi"
blk_device=mmcblk0p
;;
"raspberrypi3"|"raspberrypi0w")
mountpoint="/uboot"
blk_device=mmcblk0p
;;
"rockpro64")
mountpoint="/uboot"
blk_device=mmcblk1p
;;
"qemux86_64")
mountpoint="/boot/efi"
blk_device=hda
data_id=5
;;
*)
log "Error: unsupported device type $device_type"
exit 1
;;
esac
# Add Mender specific entries to fstab.
sudo bash -c "cat <<- EOF > ${sysconfdir}/fstab
# stock fstab - you probably want to override this with a machine specific one
/dev/root / auto defaults 1 1
proc /proc proc defaults 0 0
devpts /dev/pts devpts mode=0620,gid=5 0 0
tmpfs /run tmpfs mode=0755,nodev,nosuid,strictatime 0 0
tmpfs /var/volatile tmpfs defaults 0 0
# uncomment this if your device has a SD/MMC/Transflash slot
#/dev/mmcblk0p1 /media/card auto defaults,sync,noauto 0 0
# Where the U-Boot environment resides; for devices with SD card support ONLY!
/dev/${blk_device}1 $mountpoint auto defaults,sync 0 0
/dev/${blk_device}${data_id} /data auto defaults 0 0
EOF"
if [ "$device_type" == "qemux86_64" ]; then
# Add entry referring to swap partition.
sudo tee -a ${sysconfdir}/fstab <<< "/dev/hda6 swap swap defaults 0 0" 2>&1 >/dev/null
fi
log "\tDone."
}
# Takes following arguments
#
# $1 - path to source raw disk image
# $2 - sector start (in 512 blocks)
# $3 - size (in 512 blocks)
extract_file_from_image() {
log "\tStoring data in $4."
local cmd="dd if=$1 of=${output_dir}/$4 skip=$2 bs=512 count=$3 conv=sparse"
$(${cmd}>> "$build_log" 2>&1)
}
# Takes following arguments
#
# $1 - device type
# $2 - partition alignment in bytes
# $3 - total size in bytes
# $4 - array of partitions' sizes for Mender image
#
create_test_config_file() {
local device_type=$1
local alignment=$2
local mender_image_size_mb=$(( (($3 / 1024) / 1024) ))
local _mender_sizes=$(declare -p $4)
eval "declare -A mender_sizes="${_mender_sizes#*=}
local sector_size=${mender_sizes[sector_size]}
local boot_offset=$(( (${mender_sizes[pboot_start]} * $sector_size) ))
local boot_size_mb=$(( (((${mender_sizes[pboot_size]} * $sector_size) / 1024) / 1024) ))
local rootfs_size_mb=$(( (((${mender_sizes[prootfs_size]} * $sector_size) / 1024) / 1024) ))
local data_size_mb=$(( (((${mender_sizes[pdata_size]} * $sector_size) / 1024) / 1024) ))
cp ${files_dir}/variables.template ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_BOOT_PART_SIZE_MB/s/=.*$/="'${boot_size_mb}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_DATA_PART_SIZE_MB/s/=.*$/="'${data_size_mb}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_DEVICE_TYPE/s/=.*$/="'${device_type}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_PARTITION_ALIGNMENT/s/=.*$/="'${alignment}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_STORAGE_TOTAL_SIZE_MB/s/=.*$/="'${mender_image_size_mb}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_UBOOT_ENV_STORAGE_DEVICE_OFFSET/s/=.*$/="'${boot_offset}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_CALC_ROOTFS_SIZE/s/=.*$/="'${rootfs_size_mb}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^MENDER_MACHINE/s/=.*$/="'${device_type}'"/' ${output_dir}/${device_type}_variables.cfg
sed -i '/^DEPLOY_DIR_IMAGE/s/=.*$/="'${output_dir//\//\\/}'"/' ${output_dir}/${device_type}_variables.cfg
}
# Takes following arguments
#
# $1 - device type
# $2 - parameter name to change
# $3 - parameter value
update_test_config_file() {
local device_type=$1
[ ! -f "${output_dir}/${device_type}_variables.cfg" ] && \
{ log "Error: test configuration file '${device_type}_variables.cfg' not found. Aborting."; return 1; }
shift
while test ${#} -gt 0
do
case "$1" in
"artifact-name")
sed -i '/^MENDER_ARTIFACT_NAME/s/=.*$/="'${2}'"/' ${output_dir}/${device_type}_variables.cfg
;;
"distro-feature")
sed -i '/^DISTRO_FEATURES/s/=.*$/="'${2}'"/' ${output_dir}/${device_type}_variables.cfg
;;
"mount-location")
sed -i '/^MENDER_BOOT_PART_MOUNT_LOCATION/s/=.*$/="'${2}'"/' ${output_dir}/${device_type}_variables.cfg
;;
esac
shift 2
done
return 0
}

229
mender-convert-modify

@ -0,0 +1,229 @@
#! /usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Default that can be overridden by providing this method in a
# configuration file passed with '--config'
function platform_modify() {
true
}
function user_local_modify() {
true
}
function trap_exit() {
echo "mender-convert-modify has finished. Cleaning up..."
sudo umount -f work/boot
sudo umount -f work/rootfs
}
function trap_term() {
true
}
trap trap_term INT TERM
trap trap_exit EXIT
echo "Running $(basename $0): $@"
source modules/bootstrap.sh
source modules/disk.sh
source modules/probe.sh
# The mender_convert_config is always used and provides all the defaults
declare -a configs=("configs/mender_convert_config")
while (( "$#" )); do
case "$1" in
-o | --overlay)
overlays+="${2}"
shift 2
;;
-c | --config)
configs+=("${2}")
shift 2
;;
-d | --disk-image)
disk_image="${2}"
shift 2
;;
*)
log_fatal "Sorry, but the provided option is not supported: $1"
;;
esac
done
# Note the use of %q formatting here. This is a bash feature to add
# proper quoting to the strings so that spaces and special characters
# will be treated properly. Primarily for supporting spaces in
# pathnames and avoid splitting those into multiple parameters.
source modules/config.sh $(printf "%q " "${configs[@]}")
boot_part=$(disk_boot_part)
root_part=$(disk_root_part)
# Create mount points
mkdir -p work/boot
mkdir -p work/rootfs
sudo mount ${boot_part} work/boot
sudo mount ${root_part} work/rootfs
mkdir -p work/mender-deb/files
log_info "Installing Mender client and related files"
deb_arch=$(probe_debian_arch_name)
deb_name="mender-client_${MENDER_CLIENT_VERSION}-1_${deb_arch}.deb"
run_and_log_cmd "wget -Nq ${MENDER_STORAGE_URL}/${MENDER_CLIENT_VERSION}/dist-packages/debian/${deb_arch}/${deb_name} -P work/mender-deb"
cd work/mender-deb
run_and_log_cmd "ar -xv ${deb_name}"
run_and_log_cmd "tar -xf data.tar.xz -C files"
cd - > /dev/null 2>&1
run_and_log_cmd "sudo rsync --archive --keep-dirlinks --verbose work/mender-deb/files/ work/rootfs/"
if [ "${MENDER_ENABLE_SYSTEMD}" == "y" ]; then
run_and_log_cmd "sudo ln -sf /lib/systemd/system/mender-client.service \
work/rootfs/etc/systemd/system/multi-user.target.wants/mender-client.service"
fi
if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then
run_and_log_cmd "wget -Nq '${MENDER_GRUBENV_URL}' -P work/"
run_and_log_cmd "tar xvf work/${MENDER_GRUBENV_VERSION}.tar.gz -C work/"
if [ -z "${MENDER_GRUB_KERNEL_IMAGETYPE}" ]; then
kernel_imagetype=$(probe_kernel_in_boot_and_root)
else
kernel_imagetype="${MENDER_GRUB_KERNEL_IMAGETYPE}"
fi
if [ -z "${MENDER_GRUB_INITRD_IMAGETYPE}" ]; then
initrd_imagetype=$(probe_initrd_in_boot_and_root)
else
kernel_imagetype="${MENDER_GRUB_INITRD_IMAGETYPE}"
fi
cat <<- EOF > work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}/mender_grubenv_defines
mender_rootfsa_part=${MENDER_ROOTFS_PART_A_INDEX}
mender_rootfsb_part=${MENDER_ROOTFS_PART_B_INDEX}
mender_kernel_root_base=${MENDER_STORAGE_DEVICE}
mender_grub_storage_device=${MENDER_GRUB_STORAGE_DEVICE}
kernel_imagetype=${kernel_imagetype}
initrd_imagetype=${initrd_imagetype}
kernel_devicetree=${MENDER_KERNEL_DEVICETREE}
EOF
if [ -n "${MENDER_GRUB_KERNEL_BOOT_ARGS}" ]; then
cat <<- EOF > work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}/11_bootargs_grub.cfg
set bootargs="${MENDER_GRUB_KERNEL_BOOT_ARGS}"
EOF
fi
cd work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}
run_and_log_cmd "make 2>&1"
run_and_log_cmd "sudo make DESTDIR=../ BOOT_DIR=boot install-boot-files"
run_and_log_cmd "sudo make DESTDIR=../rootfs install-tools"
cd - > /dev/null 2>&1
# Remove conflicting boot files. These files do not necessarily effect the
# functionality, but lets get rid of them to avoid confusion.
#
# There is no Mender integration for EFI boot or systemd-boot.
sudo rm -rf work/boot/loader
sudo rm -rf work/boot/EFI/Linux
sudo rm -rf work/boot/EFI/systemd
sudo rm -rf work/boot/NvVars
log_info "Installing GRUB"
arch=$(probe_arch)
efi_name=$(probe_grub_efi_name)
efi_target_name=$(probe_grub_efi_target_name)
log_info "GRUB EFI: ${efi_target_name}"
run_and_log_cmd "wget -Nq ${MENDER_GRUB_BINARY_STORAGE_URL}/${arch}/${efi_name} -P work/"
run_and_log_cmd "wget -Nq ${MENDER_GRUB_BINARY_STORAGE_URL}/${arch}/grub-editenv -P work/"
run_and_log_cmd "sudo install -m 751 work/grub-editenv work/rootfs/usr/bin/"
run_and_log_cmd "sudo mkdir -p work/boot/EFI/BOOT"
run_and_log_cmd "sudo cp work/${efi_name} -P work/boot/EFI/BOOT/${efi_target_name}"
fi
run_and_log_cmd "sudo mkdir -p work/rootfs/data/mender"
run_and_log_cmd "sudo ln -sf /data/mender work/rootfs/var/lib/mender"
cat <<- EOF > work/mender.conf.data
{
"RootfsPartA": "${MENDER_STORAGE_DEVICE}${MENDER_ROOTFS_PART_A_INDEX}",
"RootfsPartB": "${MENDER_STORAGE_DEVICE}${MENDER_ROOTFS_PART_B_INDEX}"
}
EOF
run_and_log_cmd "sudo cp work/mender.conf.data work/rootfs/data/mender/mender.conf"
if [ -z "${MENDER_DEVICE_TYPE}" ]; then
# Observed systems who do not have this file, e.g images generated with mkosi
if [ -f work/rootfs/etc/hostname ]; then
device_type=$(cat work/rootfs/etc/hostname)
else
device_type="default"
fi
else
device_type="${MENDER_DEVICE_TYPE}"
fi
run_and_log_cmd "echo 'device_type=${device_type}' > work/device_type"
run_and_log_cmd "sudo install -m 0444 work/device_type work/rootfs/data/mender/"
run_and_log_cmd "sudo echo 'artifact_name=${MENDER_ARTIFACT_NAME}' \
> work/rootfs/etc/mender/artifact_info"
log_info "Installing a custom /etc/fstab (see work/convert.log for more info)"
if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then
boot_part_mountpoint="/boot/efi"
else
boot_part_mountpoint="/uboot"
fi
run_and_log_cmd "sudo mkdir -p work/rootfs/${boot_part_mountpoint}"
if [ "${MENDER_DATA_PART_GROWFS}" == "y" ]; then
MENDER_DATA_PART_FSTAB_OPTS="${MENDER_DATA_PART_FSTAB_OPTS},x-systemd.growfs"
fi
sudo bash -c "cat <<- EOF > work/rootfs/etc/fstab
# stock fstab - you probably want to override this with a machine specific one
/dev/root / auto defaults 1 1
proc /proc proc defaults 0 0
${MENDER_STORAGE_DEVICE}${MENDER_BOOT_PART_INDEX} ${boot_part_mountpoint} auto defaults,sync 0 0
${MENDER_STORAGE_DEVICE}${MENDER_DATA_PART_INDEX} /data auto ${MENDER_DATA_PART_FSTAB_OPTS} 0 0
EOF"
log_info "Performing platform specific modifications (if any)"
platform_modify
log_info "Performing user/local specific modifications (if any)"
user_local_modify
for overlay in "${overlays[@]}"; do
log_info "Applying rootfs overlay: ${overlay}"
run_and_log_cmd "sudo rsync --archive --keep-dirlinks --verbose ${overlay}/ work/rootfs/"
done

311
mender-convert-package

@ -0,0 +1,311 @@
#! /usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Default that can be overridden by providing this method in a
# configuration file passed with '--config'
function platform_package() {
true
}
function trap_exit() {
echo "mender-convert-package has finished. Cleaning up..."
sudo umount -f work/boot > /dev/null
sudo umount -f work/rootfs > /dev/null
}
function trap_term() {
true
}
trap trap_term INT TERM
trap trap_exit EXIT
echo "Running $(basename $0): $@"
source modules/bootstrap.sh
source modules/disk.sh
# The mender_convert_config is always used and provides all the defaults
declare -a configs=("configs/mender_convert_config")
while (( "$#" )); do
case "$1" in
-o | --overlay)
overlays+="${2}"
shift 2
;;
-c | --config)
configs+=("${2}")
shift 2
;;
-d | --disk-image)
disk_image="${2}"
shift 2
;;
*)
log_fatal "Sorry but the provided option is not supported: $1"
;;
esac
done
# Note the use of %q formatting here. This is a bash feature to add
# proper quoting to the strings so that spaces and special characters
# will be treated properly. Primarily for supporting spaces in
# pathnames and avoid splitting those into multiple parameters.
source modules/config.sh $(printf "%q " "${configs[@]}")
PARTED="/usr/bin/parted"
if [ ! -f ${PARTED} ]; then
PARTED="/sbin/parted"
fi
output_dir=work
boot_part=$(disk_boot_part)
root_part=$(disk_root_part)
# Final output
mkdir -p deploy
# Create mount points
mkdir -p work/boot
mkdir -p work/rootfs
sudo mount ${boot_part} work/boot
sudo mount ${root_part} work/rootfs
# Convert to 512 blocks
disk_image_total_sectors=$(disk_mb_to_sectors ${MENDER_STORAGE_TOTAL_SIZE_MB})
boot_part_sectors=$(disk_mb_to_sectors ${MENDER_BOOT_PART_SIZE_MB})
data_part_sectors=$(disk_mb_to_sectors ${MENDER_DATA_PART_SIZE_MB})
alignment_sectors=$((${MENDER_PARTITION_ALIGNMENT} / 512))
if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then
boot_part_start=${alignment_sectors}
overhead_sectors=$(( ${alignment_sectors} * 4 ))
else
# The two first blocks are typically reserved for U-boot environment.
boot_part_start=$(( ${alignment_sectors} * 3))
overhead_sectors=$(( ${alignment_sectors} * 6 ))
fi
# Validate boot-gap.bin size, ensuring it will fit between MBR and boot_part_start
if [ "${MENDER_COPY_BOOT_GAP}" == "y" ]; then
# Add one block for MBR
boot_gap_sectors=$(( $(stat --printf="%b" work/boot-gap.bin) + 1))
if [ ${boot_gap_sectors} -ge ${boot_part_start} ]; then
log_warn "The work/boot-gap.bin file will overwrite the boot partition"
log_fatal "Please increase MENDER_PARTITION_ALIGNMENT (2x will typically solve this))"
fi
fi
# Validate boot part size
actual_boot_size=$(du --apparent-size -s --block-size=512 ${boot_part} | cut -f 1)
if [ ${actual_boot_size} -gt ${boot_part_sectors} ]; then
log_warn "The allocated boot part size $(disk_sectors_to_mb ${boot_part_sectors}) MiB is too small."
log_warn "The actual boot part size is $(disk_sectors_to_mb ${actual_boot_size}) MiB"
log_warn "Will adjust MENDER_BOOT_PART_SIZE_MB automatically"
log_warn "Considered adjusting the configuration file to avoid this message"
boot_part_sectors=${actual_boot_size}
fi
# Make sure that the boot part sector size is aligned to
# MENDER_PARTITION_ALIGNMENT, it is possible that the boot part is extracted
# as-is from the input image, and the input image might not have used the same
# alignment as our configuration
boot_part_sectors=$(disk_align_sectors ${boot_part_sectors} ${MENDER_PARTITION_ALIGNMENT} )
# Calculate rootfs size
rootfs_part_sectors=$(((${disk_image_total_sectors} - ${data_part_sectors} - \
${boot_part_sectors} - ${overhead_sectors}) / 2))
# Make sure rootfs size is aligned to MENDER_PARTITION_ALIGNMENT
rootfs_part_sectors=$(disk_align_sectors ${rootfs_part_sectors} ${MENDER_PARTITION_ALIGNMENT} )
device_type=$(cat work/rootfs/data/mender/device_type | sed 's/[^=]*=//')
artifact_name=$(cat work/rootfs/etc/mender/artifact_info | sed 's/[^=]*=//')
image_name="${device_type}-${artifact_name}"
actual_rootfs_size=$(sudo du -s --block-size=512 work/rootfs | cut -f 1)
# KiB -> 512 sectors
image_rootfs_size_sectors=$((${IMAGE_ROOTFS_SIZE} * 2))
if [ "${IMAGE_ROOTFS_SIZE}" -eq -1 ]; then
rootfs_image_sectors="${rootfs_part_sectors}"
elif [ "${image_rootfs_size_sectors}" -lt "${actual_rootfs_size}" ]; then
rootfs_image_sectors="${actual_rootfs_size}"
else
rootfs_image_sectors="${image_rootfs_size_sectors}"
fi
rootfs_image_sectors=$((${rootfs_image_sectors} + ${IMAGE_ROOTFS_EXTRA_SPACE} * 2))
rootfs_image_sectors_overhead=$(awk -v r1="$actual_rootfs_size" "BEGIN{printf \"%.0f\", r1 * ${IMAGE_OVERHEAD_FACTOR}}")
if [ "${rootfs_image_sectors_overhead}" -gt "${rootfs_image_sectors}" ]; then
rootfs_image_sectors="${rootfs_image_sectors_overhead}"
fi
if [ ${rootfs_image_sectors} -gt ${rootfs_part_sectors} ]; then
log_warn "The calculated rootfs partition size $(disk_sectors_to_mb ${rootfs_part_sectors}) MiB is too small."
log_warn "The actual rootfs image size is $(disk_sectors_to_mb ${rootfs_image_sectors}) MiB"
log_fatal "You can try adjusting the MENDER_STORAGE_TOTAL_SIZE_MB variable to increase available space, or modify one of the variables IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_EXTRA_SPACE or IMAGE_OVERHEAD_FACTOR to reduce the size of the root filesystem."
fi
log_info "Rootfs filesystem size will be $(disk_sectors_to_mb ${rootfs_image_sectors}) MiB"
# Extract file-system type from rootfs
if file ${root_part} | grep -q ext4; then
image_fs_type="ext4"
elif file ${root_part} | grep -q XFS; then
image_fs_type="xfs"
else
log_warn "$(file work/${root_part})"
log_fatal "Could not determinate root file-system type. Aborting..."
fi
disk_create_file_system_from_folder "work/rootfs/data/" "work/data.img" \
"${data_part_sectors}" "${image_fs_type}"
# Clear this area as it will be contained in the data.img
sudo rm -rf work/rootfs/data/*
disk_create_file_system_from_folder "work/rootfs/" "work/rootfs.img" \
"${rootfs_image_sectors}" "${image_fs_type}"
log_info "Copying root filesystem image to deploy directory"
run_and_log_cmd "cp --sparse=always work/rootfs.img deploy/${image_name}.${image_fs_type}"
mender_create_artifact "${device_type}" "${artifact_name}"
log_info "Creating Mender compatible disk-image"
sdimg_path=deploy/${image_name}.sdimg
log_info "Total disk size: $(disk_sectors_to_mb ${disk_image_total_sectors}) MiB"
log_info " Boot partition $(disk_sectors_to_mb ${boot_part_sectors}) MiB"
log_info " RootFS partitions $(disk_sectors_to_mb ${rootfs_part_sectors}) MiB x 2"
log_info " Data partition $(disk_sectors_to_mb ${data_part_sectors}) MiB"
# Initialize sdcard image file
run_and_log_cmd \
"dd if=/dev/zero of=${sdimg_path} bs=512 count=0 seek=${disk_image_total_sectors} status=none"
# boot_part_start, is defined at the beginning of this file
boot_part_end=$(( ${boot_part_start} + ${boot_part_sectors} - 1 ))
rootfsa_start=$(disk_align_sectors ${boot_part_end} ${MENDER_PARTITION_ALIGNMENT} )
rootfsa_end=$(( ${rootfsa_start} + ${rootfs_part_sectors} - 1 ))
rootfsb_start=$(disk_align_sectors ${rootfsa_end} ${MENDER_PARTITION_ALIGNMENT} )
rootfsb_end=$(( ${rootfsb_start} + ${rootfs_part_sectors} - 1 ))
data_start=$(disk_align_sectors ${rootfsb_end} ${MENDER_PARTITION_ALIGNMENT} )
data_end=$(( ${data_start} + ${data_part_sectors} - 1 ))
# Create partition table. TODO: GPT support
run_and_log_cmd "${PARTED} -s ${sdimg_path} mklabel msdos"
run_and_log_cmd "${PARTED} -s ${sdimg_path} unit s mkpart primary fat32 ${boot_part_start} ${boot_part_end}"
run_and_log_cmd "${PARTED} -s ${sdimg_path} set 1 boot on"
run_and_log_cmd "${PARTED} -s ${sdimg_path} -- unit s mkpart primary ext2 ${rootfsa_start} ${rootfsa_end}"
run_and_log_cmd "${PARTED} -s ${sdimg_path} -- unit s mkpart primary ext2 ${rootfsb_start} ${rootfsb_end}"
run_and_log_cmd "${PARTED} -s ${sdimg_path} -- unit s mkpart primary ext2 ${data_start} ${data_end}"
run_and_log_cmd "${PARTED} -s ${sdimg_path} print"
# Write boot-gap
if [ "${MENDER_COPY_BOOT_GAP}" == "y" ]; then
log_info "Writing boot gap of size: ${boot_part_sectors} (sectors)"
disk_write_at_offset "${output_dir}/boot-gap.bin" "${sdimg_path}" "1"
fi
# Burn Partitions
disk_write_at_offset "${boot_part}" "${sdimg_path}" "${boot_part_start}"
disk_write_at_offset "${output_dir}/rootfs.img" "${sdimg_path}" "${rootfsa_start}"
disk_write_at_offset "${output_dir}/rootfs.img" "${sdimg_path}" "${rootfsb_start}"
disk_write_at_offset "${output_dir}/data.img" "${sdimg_path}" "${data_start}"
log_info "Performing platform specific package operations (if any)"
platform_package
# Create bmap index
if [ "${MENDER_USE_BMAP}" == "y" ]; then
BMAP_TOOL="/usr/bin/bmaptool"
if [ ! -e "${BMAP_TOOL}" ]; then
log_error "You have enabled the MENDER_USE_BMAP option, but we could not find the required 'bmaptool'"
log_fatal "You can install 'bmaptool' with: apt-get install bmap-tools (on Debian based distributions)"
fi
run_and_log_cmd "${BMAP_TOOL} create ${sdimg_path} > ${sdimg_path}.bmap"
fi
case "${MENDER_COMPRESS_DISK_IMAGE}" in
gzip)
log_info "Compressing ${sdimg_path}.gz"
run_and_log_cmd "pigz --best --force ${sdimg_path}"
;;
lzma)
log_info "Compressing ${sdimg_path}.xz"
run_and_log_cmd "pxz --best --force ${sdimg_path}"
;;
none)
:
;;
*)
log_fatal "Unknown MENDER_COMPRESS_DISK_IMAGE value: ${MENDER_COMPRESS_DISK_IMAGE}"
;;
esac
log_info "Conversion has completed! \o/"
##################### Create configuration file for tests ######################
if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then
boot_part_mountpoint="/boot/efi"
# This is the name of the DISTRO_FEATURES in Yocto
distro_feature="mender-grub"
else
boot_part_mountpoint="/uboot"
# This is the name of the DISTRO_FEATURES in Yocto
distro_feature="mender-uboot"
fi
distro_feature="${distro_feature} mender-convert"
cat <<- EOF > deploy/${image_name}.cfg
MENDER_BOOT_PART="${MENDER_STORAGE_DEVICE}${MENDER_BOOT_PART_INDEX}"
MENDER_ROOTFS_PART_A="${MENDER_STORAGE_DEVICE}${MENDER_ROOTFS_PART_A_INDEX}"
MENDER_ROOTFS_PART_B="${MENDER_STORAGE_DEVICE}${MENDER_ROOTFS_PART_B_INDEX}"
MENDER_BOOT_PART_MOUNT_LOCATION="${boot_part_mountpoint}"
MENDER_BOOT_PART_SIZE_MB="$(disk_sectors_to_mb ${boot_part_sectors})"
MENDER_DATA_PART_SIZE_MB="${MENDER_DATA_PART_SIZE_MB}"
MENDER_DEVICE_TYPE="${device_type}"
MENDER_PARTITIONING_OVERHEAD_KB="$(( (${overhead_sectors} * 512) / 1024 ))"
MENDER_PARTITION_ALIGNMENT="${MENDER_PARTITION_ALIGNMENT}"
MENDER_STORAGE_DEVICE="${MENDER_STORAGE_DEVICE}"
MENDER_STORAGE_TOTAL_SIZE_MB="${MENDER_STORAGE_TOTAL_SIZE_MB}"
MENDER_UBOOT_ENV_STORAGE_DEVICE_OFFSET="12582912"
MENDER_ARTIFACT_NAME="${artifact_name}"
DISTRO_FEATURES="${distro_feature}"
DEPLOY_DIR_IMAGE="${PWD}/deploy"
MENDER_MACHINE="${device_type}"
EOF
# Something that the tests expect to be defined (originally from Yocto)
cat <<- EOF >> deploy/${image_name}.cfg
IMAGE_FSTYPES="${image_fs_type} mender sdimg"
ARTIFACTIMG_FSTYPE="${image_fs_type}"
EOF

52
modules/bootstrap.sh

@ -0,0 +1,52 @@
#!/usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Credit to:
# https://github.com/gruntwork-io/bash-commons/blob/master/modules/bash-commons/src/bootstrap.sh
# Sets some Bash options to encourage well formed code.
# For example, some of the options here will cause the script to terminate as
# soon as a command fails. Another option will cause an error if an undefined
# variable is used.
# See: https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
# Any trap on ERR is inherited by shell functions, command substitutions, and
# commands executed in a subshell environment. The ERR trap is normally not
# inherited in such cases.
set -o errtrace
# Any trap on DEBUG and RETURN are inherited by shell functions, command
# substitutions, and commands executed in a subshell environment. The DEBUG and
# RETURN traps are normally not inherited in such cases.
set -o functrace
# Exit if any command exits with a non-zero exit status.
set -o errexit
# Exit if script uses undefined variables.
set -o nounset
# Prevent masking an error in a pipeline.
# Look at the end of the 'Use set -e' section for an excellent explanation.
# see: https://www.davidpashley.com/articles/writing-robust-shell-scripts/
set -o pipefail
# Make debugging easier when you use `set -x`
# See: http://wiki.bash-hackers.org/scripting/debuggingtips#making_xtrace_more_useful
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
source modules/log.sh
source modules/run.sh

22
modules/config.sh

@ -0,0 +1,22 @@
#!/usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Read in the array of config files to process
read -a configs <<< "${@}"
for config in "${configs[@]}"; do
log_info "Using configuration file: ${config}"
source "${config}"
done

176
modules/disk.sh

@ -0,0 +1,176 @@
#!/usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Print desired information about a partition
#
# Example usage:
#
# part_one_start=$(disk_get_part_value ${disk_image_path} 1 START)
#
# $1 - path to disk image
# $2 - part number
# $3 - information to be printed
#
# We use 'partx' to parse input disk image and it supports the following fields:
#
# NR partition number
# START start of the partition in sectors
# END end of the partition in sectors
# SECTORS number of sectors
# SIZE human readable size
# NAME partition name
# UUID partition UUID
# TYPE partition type (a string, a UUID, or hex)
# FLAGS partition flags
# SCHEME partition table type (dos, gpt, ...)
disk_get_part_value() {
echo "$(partx -o ${3} -g -r --nr ${2} ${1})"
}
# Prints number of partitions found in disk image
#
# Example usage:
#
# nr_of_parts=$(disk_get_nr_of_parts ${disk_image_path})
#
# $1 - path to disk image
disk_get_nr_of_parts() {
echo "$(partx -l ${1} | wc -l)"
}
# Extract a file system image from a disk image
#
# $1 - path to disk image
# $2 - sector start (in 512 blocks)
# $3 - size (in 512 blocks)
# $4 - path to output file
disk_extract_part() {
run_and_log_cmd "dd if=$1 of=$4 skip=$2 bs=512 count=$3 conv=sparse status=none"
}
# Convert MiB to number of 512 sectors
#
# $1 - MiB value
disk_mb_to_sectors() {
echo "$(( (${1} * 1024 * 1024) / 512 ))"
}
# Convert 512 sectors to MiB
#
# $1 - number of 512 sectors
disk_sectors_to_mb() {
echo "$(( (${1} * 512) / 1024 / 1024 ))"
}
# Align value (result is number of 512 sectors)
#
# $1 - value to align (number of 512 sectors)
# $2 - alignment in bytes
disk_align_sectors() {
local size_in_bytes=$(( ${1} * 512))
local reminder=$(( ${size_in_bytes} % ${2} ))
if [ $reminder -ne 0 ]; then
size_in_bytes=$(( $size_in_bytes - $reminder + ${2} ))
fi
echo "$(( $size_in_bytes / 512 ))"
}
# Write file at offset of another file
#
# $1 - file to write
# $2 - destination file
# $3 - offset in number of 512 sectors
disk_write_at_offset() {
run_and_log_cmd "dd if=${1} of=${2} seek=${3} conv=sparse status=none"
}
# Create file system image from directory content
#
# $1 - base folder
# $2 - destination file
# $3 - image size in sectors
# $4 - file system type, e.g ext4, xfs, ...
disk_create_file_system_from_folder() {
log_info "Creating a file-system image from: ${1}"
run_and_log_cmd "dd if=/dev/zero of=${2} seek=${3} count=0 bs=512 status=none"
case "$4" in
"ext4")
MKFS_EXT4="/usr/bin/mkfs.ext4"
if [ ! -f ${MKFS_EXT4} ]; then
MKFS_EXT4="/sbin/mkfs.ext4"
fi
run_and_log_cmd "${MKFS_EXT4} -q -F ${2}"
;;
"xfs")
MKFS_XFS="/usr/bin/mkfs.xfs"
if [ ! -f ${MKFS_XFS} ]; then
MKFS_XFS="/sbin/mkfs.xfs"
fi
run_and_log_cmd "${MKFS_XFS} -q -f ${2}"
;;
*)
log_fatal "Unknown file system type specified: ${4}"
;;
esac
run_and_log_cmd "mkdir -p work/output"
run_and_log_cmd "sudo mount ${2} work/output"
run_and_log_cmd "sudo rsync --archive --delete ${1} work/output/"
run_and_log_cmd "sudo umount work/output"
}
# Print path to the boot partition filesystem image
#
disk_boot_part() {
# Why this little dance you might wonder.
#
# Some disk images do not have a boot partition, but our integration does
# require one and this is why there is a difference here.
#
# If input image did not have a boot part it will be generated and be called:
#
# work/boot-generated.vfat
#
# This is mostly done to make it obvious that we have created an empty vfat file.
#
# But if the disk image already had a boot part, we assume that it is the first
# partition that was extracted.
#
# We also need to adjust the part number of the rootfs part depending on if
# boot part was extracted or generated.
boot_part="work/boot-generated.vfat"
if [ ! -f ${boot_part} ]; then
boot_part="work/part-1.fs"
fi
echo "${boot_part}"
}
# Print path to the root partition filesystem image
#
disk_root_part() {
boot_part="work/boot-generated.vfat"
if [ ! -f ${boot_part} ]; then
root_part="work/part-2.fs"
else
root_part="work/part-1.fs"
fi
echo "${root_part}"
}

67
modules/log.sh

@ -0,0 +1,67 @@
#!/usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This must be an absolute path, as users might call the log functions
# from sub-directories
log_file="${PWD}/work/convert.log"
# Log the given message at the given level.
function log {
local -r level="$1"
local -r message="$2"
local -r timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local -r script_name="$(basename "$0")"
>&2 echo -e "${timestamp} [${level}] [$script_name] ${message}" | tee -a ${log_file}
}
function local_log_debug {
local -r level="DEBUG"
local -r message="$1"
local -r timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local -r script_name="$(basename "$0")"
echo -e "${timestamp} [${level}] [$script_name] ${message}" >> ${log_file}
}
# Log the given message at DEBUG level.
function log_debug {
local -r message="$1"
local_log_debug "$message"
}
# Log the given message at INFO level.
function log_info {
local -r message="$1"
log "INFO" "$message"
}
# Log the given message at WARN level.
function log_warn {
local -r message="$1"
log "WARN" "$message"
}
# Log the given message at ERROR level.
function log_error {
local -r message="$1"
log "ERROR" "$message"
}
# Log the given message at FATAL level.
function log_fatal {
local -r message="$1"
log "FATAL" "$message"
exit 1
}

239
modules/probe.sh

@ -0,0 +1,239 @@
#!/usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Prints target architecture
#
# No input parameters and these work on the assumption that boot and root parts
# are mounted at work/boot and work/rootfs
probe_arch() {
# --dereference, means to follow symlinks because 'ls' could be a symlink
# to busybox
file_info=""
for location in bin/ls usr/bin/ls; do
if [ -e work/rootfs/${location} ]; then
file_info=$(file -b --dereference work/rootfs/${location})
break
fi
done
if [ -z "${file_info}" ]; then
log_fatal "Sorry, where not able to determinate target architecture"
fi
target_arch="unknown"
if grep -q x86-64 <<< "${file_info}"; then
target_arch="x86-64"
elif grep -Eq "ELF 32-bit.*ARM" <<< "${file_info}"; then
target_arch="arm"
elif grep -Eq "ELF 64-bit.*aarch64" <<< "${file_info}"; then
target_arch="aarch64"
else
log_fatal "Unsupported architecture: ${file_info}"
fi
echo "${target_arch}"
}
# Prints GRUB EFI name depending on target architecture
#
# No input parameters and these work on the assumption that boot and root parts
# are mounted at work/boot and work/rootfs
probe_grub_efi_name() {
efi_name=""
arch=$(probe_arch)
case "${arch}" in
"x86-64")
efi_name="grub-efi-bootx64.efi"
;;
"arm")
efi_name="grub-efi-bootarm.efi"
;;
"aarch64")
efi_name="grub-efi-bootaa64.efi"
;;
*)
log_fatal "Unknown arch: ${arch}"
;;
esac
echo "$efi_name"
}
# Prints Debian arch name depending on target architecture
#
# No input parameters and these work on the assumption that boot and root parts
# are mounted at work/boot and work/rootfs
probe_debian_arch_name() {
deb_arch=""
arch=$(probe_arch)
case "${arch}" in
"x86-64")
deb_arch="amd64"
;;
"arm")
deb_arch="armhf"
;;
"aarch64")
deb_arch="arm64"
;;
*)
log_fatal "Unknown arch: ${arch}"
;;
esac
echo "${deb_arch}"
}
# Prints GRUB EFI target name depending on target architecture
#
# This is what the file name should be when put on target boot part.
#
# No input parameters and these work on the assumption that boot and root parts
# are mounted at work/boot and work/rootfs
probe_grub_efi_target_name() {
efi_target_name=""
arch=$(probe_arch)
case "$arch" in
"x86-64")
efi_target_name="bootx64.efi"
;;
"arm")
efi_target_name="bootarm.efi"
;;
"aarch64")
efi_target_name="bootaa64.efi"
;;
*)
log_fatal "Unknown arch: ${arch}"
;;
esac
echo "$efi_target_name"
}
# Prints path to the Linux kernel image
#
# $1 - directory in which the search is performed
#
probe_kernel_image() {
kernel_image_path=""
for image in vmlinuz zImage bzImage; do
# Linux kernel image type and naming varies between different platforms.
#
# The wildcard at the end is important, because it is common to suffix the
# Linux kernel version to the image type/name, e.g:
#
# vmlinuz-4.14-x86_64
# vmlinuz-3.10.0-862.el7.x86_64
# vmlinuz-4.15.0-20-generic
#
kernel_image_path=$(sudo find ${1} -name ${image}* ! -name '*-rescue-*')
if [ -n "${kernel_image_path}" ]; then
break
fi
done
echo "${kernel_image_path}"
}
# Prints path to the initrd/initramfs image
#
# $1 - directory in which the search is performed
#
probe_initrd_image() {
initrd_image_path=""
for image in initramfs initrd; do
# initrd/initramfs naming varies between different platforms.
#
# The wildcard at the end is important, because it is common to suffix the
# Linux kernel version to the image name, e.g:
#
# initrd.img-4.15.0-20-generic
#
initrd_image_path=$(sudo find ${1} -name ${image}* ! -name '*-rescue-*')
if [ -n "${initrd_image_path}" ]; then
break
fi
done
echo "${initrd_image_path}"
}
# Prints Linux kernel image name
#
# It will look for it in both boot and rootfs parts. If image is only present
# in boot part, it will move it to rootfs/boot
#
# No input parameters and these work on the assumption that boot and root parts
# are mounted at work/boot and work/rootfs
probe_kernel_in_boot_and_root() {
kernel_imagetype_path=""
# Important to check rootfs/boot first, because it might be possible that
# they are stored in both partitions, and in this case we want to find the
# image in the rootfs first and use that, to avoid copying it over from
# boot part when it is already there.
for boot in work/rootfs/boot work/boot; do
kernel_imagetype_path=$(probe_kernel_image ${boot})
if [ -n "${kernel_imagetype_path}" ] && [ "${boot}" == "work/boot" ]; then
log_info "Found Linux kernel image in boot part, moving to rootfs/boot"
sudo cp ${kernel_imagetype_path} work/rootfs/boot
break;
elif [ -n "${kernel_imagetype_path}" ]; then
break;
fi
done
if [ -n "${kernel_imagetype_path}" ]; then
log_info "Found Linux kernel image: \n\n\t${kernel_imagetype_path}\n"
kernel_imagetype=$(basename ${kernel_imagetype_path})
else
log_warn "Unfortunately we where not able to find the Linux kernel image."
log_fatal "Please specify the image name using MENDER_GRUB_KERNEL_IMAGETYPE"
fi
echo "${kernel_imagetype}"
}
# Prints initrd/initramfs image name
#
# It will look for it in both boot and rootfs parts. If image is only present
# in boot part, it will move it to rootfs/boot
#
# No input parameters and these work on the assumption that boot and root parts
# are mounted at work/boot and work/rootfs
probe_initrd_in_boot_and_root() {
initrd_image_path=""
# Important to check rootfs/boot first, because it might be possible that
# they are stored in both partitions, and in this case we want to find the
# image in the rootfs first and use that, to avoid copying it over from
# boot part when it is already there.
for boot in work/rootfs/boot work/boot; do
initrd_image_path=$(probe_initrd_image ${boot})
if [ -n "${initrd_image_path}" ] && [ "${boot}" == "work/boot" ]; then
sudo cp ${initrd_image_path} ${target_rootfs_dir}/boot
break;
elif [ -n "${initrd_image_path}" ]; then
break;
fi
done
if [ -n "${initrd_image_path}" ]; then
log_info "Found initramfs image: \n\n\t${initrd_image_path}\n"
initrd_imagetype=$(basename ${initrd_image_path})
else
log_info "Unfortunately we where not able to find the initrd image."
log_info "Please specify the image name using MENDER_GRUB_INITRD_IMAGETYPE \
(only required if your board is using this)"
initrd_imagetype=""
fi
echo "${initrd_imagetype}"
}

28
modules/run.sh

@ -0,0 +1,28 @@
#!/usr/bin/env bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Run a command, capture output and log it
#
# $1 - command to run
function run_and_log_cmd() {
local -r cmd="${1}"
log_debug "Running: \n\r\n\r\t ${cmd}"
log_debug "Run result: \n\r"
result=$(eval ${cmd})
log_debug "${result}"
}

21
qemux86_64-convert-stage-2.sh

@ -1,21 +0,0 @@
#!/bin/bash
output_dir=$1
boot_mapping=$2
build_log=$output_dir/build.log
[ ! -f $output_dir/boot.vfat ] && \
{ log "Error: extracted boot partition not found. Aborting."; exit 1; }
# Make a copy of Linux kernel arguments and modify.
mcopy -on -i ${output_dir}/boot.vfat -s ::EFI/BOOT/grub.cfg ${output_dir}/grub.cfg
sed -i 's/\b[ ]root=[^ ]*/ root=\/dev\/hda2/' ${output_dir}/grub.cfg
# Update Linux kernel command arguments with our custom configuration
mcopy -o -i ${output_dir}/boot.vfat -s ${output_dir}/grub.cfg ::EFI/BOOT/grub.cfg
sudo dd if=${output_dir}/boot.vfat of=/dev/mapper/${boot_mapping} bs=1M conv=sparse >> "$build_log" 2>&1
log "\tDone."
exit 0

1
requirements-deb.txt

@ -0,0 +1 @@
binutils xz-utils file rsync parted e2fsprogs xfsprogs pigz dosfstools wget git make bmap-tools

22
rockpro64-convert-stage-2.sh

@ -1,22 +0,0 @@
#!/bin/bash
output_dir=$1
boot_mapping=$2
embedded_rootfs_dir=$3
uboot_backup_dir=${embedded_rootfs_dir}/opt/backup/uboot
build_log=$output_dir/build.log
boot_part_dev="/dev/mapper/${boot_mapping}"
[ ! -f $output_dir/boot.vfat ] && \
{ log "Error: extracted boot partition not found. Aborting."; exit 1; }
[ ! -d "${embedded_rootfs_dir}" ] && \
{ log "Error: embedded content not mounted."; exit 1; }
[ ! -e ${boot_part_dev} ] && \
{ log "Error: boot part does not exist: ${boot_part_dev}."; exit 1; }
sudo dd if=${output_dir}/boot.vfat of=${boot_part_dev} bs=1M conv=sparse >> "$build_log" 2>&1
log "\tDone."
exit 0

0
rootfs_overlay_demo/.gitkeep

14
rpi-convert-stage-2.sh

@ -1,14 +0,0 @@
#!/bin/bash
output_dir=$1
boot_mapping=$2
build_log=$output_dir/build.log
[ ! -f $output_dir/boot.vfat ] && \
{ log "Error: extracted boot partition not found. Aborting."; exit 1; }
sudo dd if=${output_dir}/boot.vfat of=/dev/mapper/${boot_mapping} bs=1M conv=sparse >> "$build_log" 2>&1
log "\tDone."
exit 0

258
rpi-convert-stage-5.sh

@ -1,258 +0,0 @@
#!/bin/bash
# Copyright 2018 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
application_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
files_dir=${application_dir}/files
output_dir=${application_dir}/output
uboot_dir=${output_dir}/uboot-mender
bin_base_dir=${output_dir}/bin
bin_dir_pi=${bin_base_dir}/raspberrypi
sdimg_base_dir=$output_dir/sdimg
build_log=${output_dir}/build.log
declare -a mender_disk_mappings
declare -a mender_partitions_regular=("boot" "primary" "secondary" "data")
# Takes following arguments:
#
# $1 - ARM toolchain
# $2 - RPI machine (raspberrypi3 or raspberrypi0w)
build_uboot_files() {
local CROSS_COMPILE=${1}-
local ARCH=arm
local branch="mender-rpi-2018.07"
local commit="50f05ab10e"
local uboot_repo_vc_dir=$uboot_dir/.git
local defconfig="rpi_3_32b_defconfig"
if [ "$2" == "raspberrypi0w" ]; then
defconfig="rpi_0_w_defconfig"
fi
export CROSS_COMPILE=$CROSS_COMPILE
export ARCH=$ARCH
mkdir -p $bin_dir_pi
log "\tBuilding U-Boot related files."
if [ ! -d $uboot_repo_vc_dir ]; then
git clone https://github.com/mendersoftware/uboot-mender.git -b $branch >> "$build_log" 2>&1
fi
cd $uboot_dir
git checkout $commit >> "$build_log" 2>&1
make --quiet distclean >> "$build_log"
make --quiet $defconfig >> "$build_log" 2>&1
make --quiet >> "$build_log" 2>&1
make --quiet envtools >> "$build_log" 2>&1
cat<<-'EOF' >boot.cmd
fdt addr ${fdt_addr} && fdt get value bootargs /chosen bootargs
run mender_setup
mmc dev ${mender_uboot_dev}
if load ${mender_uboot_root} ${kernel_addr_r} /boot/zImage; then
bootz ${kernel_addr_r} - ${fdt_addr}
elif load ${mender_uboot_root} ${kernel_addr_r} /boot/uImage; then
bootm ${kernel_addr_r} - ${fdt_addr}
else
echo "No bootable Kernel found."
fi
run mender_try_to_recover
EOF
if [ ! -e $uboot_dir/tools/mkimage ]; then
log "Error: cannot build U-Boot. Aborting"
return 1
fi
$uboot_dir/tools/mkimage -A arm -T script -C none -n "Boot script" -d "boot.cmd" boot.scr >> "$build_log" 2>&1
cp -t $bin_dir_pi $uboot_dir/boot.scr $uboot_dir/tools/env/fw_printenv $uboot_dir/u-boot.bin
return 0
}
# Takes following arguments:
#
# $1 - boot partition mountpoint
# $2 - primary partition mountpoint
install_files() {
local boot_dir=$1
local rootfs_dir=$2
local kernel_img="kernel7.img"
if [ "${device_type}" == "raspberrypi0w" ]; then
kernel_img="kernel.img"
fi
log "\tInstalling U-Boot related files."
# Make a copy of Linux kernel arguments and modify.
sudo cp ${boot_dir}/cmdline.txt ${output_dir}/cmdline.txt
sed -i 's/\b[ ]root=[^ ]*/ root=\${mender_kernel_root}/' ${output_dir}/cmdline.txt
# Original Raspberry Pi image run once will have init_resize.sh script removed
# from the init argument from the cmdline.
#
# On the other hand in Mender image we want to retain a mechanism of last
# partition resizing. Check the cmdline.txt file and add it back if necessary.
if ! grep -q "init=/usr/lib/raspi-config/init_resize.sh" ${output_dir}/cmdline.txt; then
cmdline=$(cat ${output_dir}/cmdline.txt)
sh -c -e "echo '${cmdline} init=/usr/lib/raspi-config/init_resize.sh' > ${output_dir}/cmdline.txt";
fi
# Update Linux kernel command arguments with our custom configuration
sudo cp ${output_dir}/cmdline.txt ${boot_dir}
# Mask udisks2.service, otherwise it will mount the inactive part and we
# might write an update while it is mounted which often result in
# corruptions.
#
# TODO: Find a way to only blacklist mmcblk0pX devices instead of masking
# the service.
sudo ln -sf /dev/null ${rootfs_dir}/etc/systemd/system/udisks2.service
# Extract Linux kernel and install to /boot directory on rootfs
sudo cp ${boot_dir}/${kernel_img} ${rootfs_dir}/boot/zImage
# Replace kernel with U-boot and add boot script
sudo mkdir -p ${rootfs_dir}/uboot
sudo cp ${bin_dir_pi}/u-boot.bin ${boot_dir}/${kernel_img}
sudo cp ${bin_dir_pi}/boot.scr ${boot_dir}
# Raspberry Pi configuration files, applications expect to find this on
# the device and in some cases parse the options to determinate
# functionality.
sudo ln -fs /uboot/config.txt ${rootfs_dir}/boot/config.txt
sudo install -m 755 ${bin_dir_pi}/fw_printenv ${rootfs_dir}/sbin/fw_printenv
sudo ln -fs /sbin/fw_printenv ${rootfs_dir}/sbin/fw_setenv
# Override init script to expand the data partition instead of rootfs, which it
# normally expands in standard Raspberry Pi distributions.
sudo install -m 755 ${files_dir}/init_resize.sh \
${rootfs_dir}/usr/lib/raspi-config/init_resize.sh
# As the whole process must be conducted in two steps, i.e. resize partition
# during first boot and resize the partition's file system on system's first
# start-up add systemd service file and script.
sudo install -m 644 ${files_dir}/resizefs.service \
${rootfs_dir}/lib/systemd/system/resizefs.service
sudo ln -sf /lib/systemd/system/resizefs.service \
${rootfs_dir}/etc/systemd/system/multi-user.target.wants/resizefs.service
sudo install -m 755 ${files_dir}/resizefs.sh \
${rootfs_dir}/usr/sbin/resizefs.sh
# Remove original 'resize2fs_once' script and its symbolic link.
sudo unlink ${rootfs_dir}/etc/rc3.d/S01resize2fs_once
sudo rm ${rootfs_dir}/etc/init.d/resize2fs_once
}
do_install_bootloader() {
if [ -z "${mender_disk_image}" ]; then
log "Mender raw disk image file not set. Aborting."
exit 1
fi
if [ -z "${bootloader_toolchain}" ]; then
log "ARM GCC toolchain not set. Aborting."
exit 1
fi
if ! [ -x "$(command -v ${bootloader_toolchain}-gcc)" ]; then
log "Error: ARM GCC not found in PATH. Aborting."
exit 1
fi
[ ! -f $mender_disk_image ] && \
{ log "$mender_disk_image - file not found. Aborting."; exit 1; }
# Map & mount Mender compliant image.
create_device_maps $mender_disk_image mender_disk_mappings
# Change current directory to 'output' directory.
cd $output_dir
# Build patched U-Boot files.
build_uboot_files $bootloader_toolchain $device_type
rc=$?
cd $output_dir
if [ $rc -eq 0 ]; then
mount_mender_disk ${mender_disk_mappings[@]}
install_files ${output_dir}/sdimg/boot ${output_dir}/sdimg/primary
fi
detach_device_maps ${mender_disk_mappings[@]}
rm -rf $sdimg_base_dir
[[ $keep -eq 0 ]] && { rm -f ${output_dir}/config.txt ${output_dir}/cmdline.txt;
rm -rf $uboot_dir $bin_base_dir; }
[[ "$rc" -ne 0 ]] && { exit 1; } || { log "\tDone."; }
}
# Conditional once we support other boards
PARAMS=""
while (( "$#" )); do
case "$1" in
-m | --mender-disk-image)
mender_disk_image=$2
shift 2
;;
-b | --bootloader-toolchain)
bootloader_toolchain=$2
shift 2
;;
-d | --device-type)
device_type=$2
shift 2
;;
-k | --keep)
keep=1
shift 1
;;
-h | --help)
show_help
exit 0
;;
--)
shift
break
;;
-*)
log "Error: unsupported option $1"
exit 1
;;
*)
PARAMS="$PARAMS $1"
shift
;;
esac
done
eval set -- "$PARAMS"
# Some commands expect elevated privileges.
sudo true
do_install_bootloader

16
scripts/README-run-tests.md

@ -0,0 +1,16 @@
# Run tests
The tests utilize the provided Docker container in this repository and conversion
is done using the `docker-mender-convert` command. For this reason we first need
to build the container (commands must be run from the root directory of
`mender-convert`):
```bash
./docker-build
```
Run tests:
```bash
./scripts/run-tests.sh
```

77
scripts/bootstrap-rootfs-overlay-demo-server.sh

@ -0,0 +1,77 @@
#!/bin/bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Exit if any command exits with a non-zero exit status.
set -o errexit
root_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )
if [ "${root_dir}" != "${PWD}" ]; then
echo "You must execute $(basename $0) from the root directory: ${root_dir}"
exit 1
fi
server_ip=""
output_dir=""
while (( "$#" )); do
case "$1" in
-o | --output-dir)
output_dir="${2}"
shift 2
;;
-s | --server-ip)
server_ip="${2}"
shift 2
;;
*)
echo "Sorry but the provided option is not supported: $1"
echo "Usage: $(basename $0) --output-dir ./rootfs_overlay_demo --server-ip <your server IP address>"
exit 1
;;
esac
done
if [ -z "${output_dir}" ]; then
echo "Sorry, but you need to provide an output directory using the '-o/--output-dir' option"
exit 1
fi
if [ -z "${server_ip}" ]; then
echo "Sorry, but you need to provide a server IP address using the '-s/--server-ip' option"
exit 1
fi
mkdir -p ${output_dir}/etc/mender
cat <<- EOF > ${output_dir}/etc/mender/mender.conf
{
"InventoryPollIntervalSeconds": 5,
"RetryPollIntervalSeconds": 30,
"ServerURL": "https://docker.mender.io",
"ServerCertificate": "/etc/mender/server.crt",
"UpdatePollIntervalSeconds": 5
}
EOF
cat <<- EOF > ${output_dir}/etc/hosts
127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
${server_ip} docker.mender.io s3.docker.mender.io
EOF
wget -q "https://raw.githubusercontent.com/mendersoftware/mender/master/support/demo.crt" -O ${output_dir}/etc/mender/server.crt
echo "Configuration file for using Demo Mender Server written to: ${output_dir}/etc/mender"

67
scripts/bootstrap-rootfs-overlay-hosted-server.sh

@ -0,0 +1,67 @@
#!/bin/bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Exit if any command exits with a non-zero exit status.
set -o errexit
root_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )
if [ "${root_dir}" != "${PWD}" ]; then
echo "You must execute $(basename $0) from the root directory: ${root_dir}"
exit 1
fi
tenant_token=""
output_dir=""
while (( "$#" )); do
case "$1" in
-t | --tenant-token)
tenant_token="${2}"
shift 2
;;
-o | --output-dir)
output_dir="${2}"
shift 2
;;
*)
echo "Sorry but the provided option is not supported: $1"
echo "Usage: $(basename $0) --output-dir ./rootfs_overlay_demo --tenant-token <paste your token here>"
exit 1
;;
esac
done
if [ -z "${output_dir}" ]; then
echo "Sorry, but you need to provide an output directory using the '-o/--output-dir' option"
exit 1
fi
if [ -z "${tenant_token}" ]; then
echo "Sorry, but you need to provide a tenant token using the '-t/--tenant-token' option"
exit 1
fi
mkdir -p ${output_dir}/etc/mender
cat <<- EOF > ${output_dir}/etc/mender/mender.conf
{
"InventoryPollIntervalSeconds": 5,
"RetryPollIntervalSeconds": 30,
"ServerURL": "https://hosted.mender.io/",
"TenantToken": "${tenant_token}",
"UpdatePollIntervalSeconds": 5
}
EOF
echo "Configuration file for using Hosted Mender written to: ${output_dir}/etc/mender"

80
scripts/bootstrap-rootfs-overlay-production-server.sh

@ -0,0 +1,80 @@
#!/bin/bash
#
# Copyright 2019 Northern.tech AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Exit if any command exits with a non-zero exit status.
set -o errexit
root_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )
if [ "${root_dir}" != "${PWD}" ]; then
echo "You must execute $(basename $0) from the root directory: ${root_dir}"
exit 1
fi
server_url=""
output_dir=""
while (( "$#" )); do
case "$1" in
-o | --output-dir)
output_dir="${2}"
shift 2
;;
-s | --server-url)
server_url="${2}"
shift 2
;;
-S | --server-cert)
server_cert="${2}"
shift 2
;;
*)
echo "Sorry but the provided option is not supported: $1"
echo "Usage: $(basename $0) --output-dir ./rootfs_overlay_demo --server-url <your server URL> [--server-cert <path to your server.crt file>]"
exit 1
;;
esac
done
if [ -z "${output_dir}" ]; then
echo "Sorry, but you need to provide an output directory using the '-o/--output-dir' option"
exit 1
fi
if [ -z "${server_url}" ]; then
echo "Sorry, but you need to provide a server URL using the '-s/--server-url' option"
exit 1
fi
mkdir -p ${output_dir}/etc/mender
cat <<- EOF > ${output_dir}/etc/mender/mender.conf
{
"InventoryPollIntervalSeconds": 5,
"RetryPollIntervalSeconds": 30,
"ServerURL": "${server_url}",
EOF
if [ -n "${server_cert}" ]; then
cat <<- EOF >> ${output_dir}/etc/mender/mender.conf
"ServerCertificate": "/etc/mender/server.crt",
EOF
cp -f "${server_cert}" ${output_dir}/etc/mender/server.crt
fi
cat <<- EOF >> ${output_dir}/etc/mender/mender.conf
"UpdatePollIntervalSeconds": 5
}
EOF
echo "Configuration file for using Production Mender Server written to: ${output_dir}/etc/mender"

105
scripts/test/run-tests.sh

@ -0,0 +1,105 @@
#!/bin/bash
set -e
usage() {
echo "$0 [--no-pull] <--all | --prebuilt-image DEVICE_TYPE IMAGE_NAME>"
exit 1
}
root_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../../" && pwd )
if [ "${root_dir}" != "${PWD}" ]; then
echo "You must execute $(basename $0) from the root directory: ${root_dir}"
exit 1
fi
WORKSPACE=./tests
BBB_DEBIAN_IMAGE="bone-debian-9.5-iot-armhf-2018-10-07-4gb.img"
BBB_DEBIAN_IMAGE_URL="http://debian.beagleboard.org/images/${BBB_DEBIAN_IMAGE}.xz"
RASPBIAN_IMAGE="2019-09-26-raspbian-buster-lite"
RASPBIAN_IMAGE_URL="http://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-09-30/2019-09-26-raspbian-buster-lite.zip"
TINKER_IMAGE="20170417-tinker-board-linaro-stretch-alip-v1.8"
TINKER_IMAGE_URL="http://dlcdnet.asus.com/pub/ASUS/mb/Linux/Tinker_Board_2GB/${TINKER_IMAGE}.zip"
UBUNTU_IMAGE="Ubuntu-Bionic-x86-64.img"
UBUNTU_IMAGE_URL="https://d1b0l86ne08fsf.cloudfront.net/mender-convert/images/${UBUNTU_IMAGE}.gz"
UBUNTU_SERVER_RPI_IMAGE="ubuntu-18.04.3-preinstalled-server-armhf+raspi3.img"
UBUNTU_SERVER_RPI_IMAGE_URL="http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/${UBUNTU_SERVER_RPI_IMAGE}.xz"
# Keep common function declarations in separate utils script
UTILS_PATH=${0/$(basename $0)/test-utils.sh}
source $UTILS_PATH
# Some distros do not have /sbin in path for "normal users"
export PATH="${PATH}:/sbin"
if [ "$1" = "--no-pull" ]; then
shift
else
echo "Automatically pulling submodules. Use --no-pull to disable"
git submodule update --init --remote
fi
mkdir -p ${WORKSPACE}
get_pytest_files
test_result=0
case "$1" in
--prebuilt-image)
if [ -z "$3" ]; then
echo "Both DEVICE_TYPE and IMAGE_NAME must be specified"
exit 1
fi
test_result=0
run_tests "$2" "$3" || test_result=$?
exit $test_result
;;
--all)
convert_and_test "qemux86_64" \
"release-1" \
"${UBUNTU_IMAGE_URL}" \
"${UBUNTU_IMAGE}" \
"${UBUNTU_IMAGE}.gz" \
"configs/qemux86-64_config" || test_result=$?
convert_and_test "raspberrypi" \
"release-1" \
"${RASPBIAN_IMAGE_URL}" \
"${RASPBIAN_IMAGE}.img" \
"${RASPBIAN_IMAGE}.zip" \
"configs/raspberrypi3_config" || test_result=$?
# MEN-2809: Disabled due broken download link
#convert_and_test "linaro-alip" \
# "release-1" \
# "${TINKER_IMAGE_URL}" \
# "${TINKER_IMAGE}.img" \
# "${TINKER_IMAGE}.zip" || test_result=$?
convert_and_test "beaglebone" \
"release-1" \
"${BBB_DEBIAN_IMAGE_URL}" \
"${BBB_DEBIAN_IMAGE}" \
"${BBB_DEBIAN_IMAGE}.xz" || test_result=$?
convert_and_test "ubuntu" \
"release-1" \
"${UBUNTU_SERVER_RPI_IMAGE_URL}" \
"${UBUNTU_SERVER_RPI_IMAGE}" \
"${UBUNTU_SERVER_RPI_IMAGE}.xz" \
"configs/raspberrypi3_config" || test_result=$?
exit $test_result
;;
*)
usage
;;
esac

114
scripts/test/test-utils.sh

@ -0,0 +1,114 @@
MENDER_ACCEPTANCE_URL="https://raw.githubusercontent.com/mendersoftware/meta-mender/master/tests/acceptance"
WORKSPACE=${WORKSPACE:-./tests}
MENDER_CONVERT_DIR=$PWD
convert_and_test() {
device_type=$1
artifact_name=$2
image_url=$3
image_name=$4
image_name_compressed=$5
config=$6 # Optional
wget --progress=dot:giga -N ${image_url} -P input/
echo "Extracting: ${image_name_compressed}"
case "${image_name_compressed}" in
*.gz)
gunzip -f input/${image_name_compressed}
;;
*.zip)
cd input
unzip -o ${image_name_compressed}
cd -
;;
*.xz)
xz -d -f input/${image_name_compressed}
;;
*)
echo "Unknown image type: ${image_name_compressed}"
exit 1
;;
esac
rm -f ${WORKSPACE}/test_config
# Two motives for the following statement
#
# - speed up tests by avoiding decompression on all images (majority of images
# we test have a platform specific configuration)
#
# - test providing multiple '--config' options
#
# - (when no platform configuration is provided) test conversion without
# '--config' and with MENDER_COMPRESS_DISK_IMAGE=gzip. Compressed disk
# images is the default user facing option and we need to ensure that we
# cover this in the tests.
if [ -n "${config}" ]; then
echo "Will disable MENDER_COMPRESS_DISK_IMAGE for this image"
echo "MENDER_COMPRESS_DISK_IMAGE=none" > ${WORKSPACE}/test_config
local MENDER_CONVERT_EXTRA_ARGS="--config ${config} --config ${WORKSPACE}/test_config"
fi
MENDER_ARTIFACT_NAME=${artifact_name} ./docker-mender-convert \
--disk-image input/${image_name} \
${MENDER_CONVERT_EXTRA_ARGS}
local ret=0
run_tests "${device_type}" "${artifact_name}" || ret=$?
rm -f deploy/${device_type}-${artifact_name}.*
return $ret
}
run_tests() {
device_type=$1
artifact_name=$2
shift 2
pytest_args_extra=$@
if pip3 list | grep -q -e pytest-html; then
html_report_args="--html=${MENDER_CONVERT_DIR}/report_${device_type}.html --self-contained-html"
fi
# Need to decompress images built with MENDER_COMPRESS_DISK_IMAGE=gzip before
# running tests.
if [ -f deploy/${device_type}-${artifact_name}.sdimg.gz ]; then
# sudo is needed because the image is created using docker-mender-convert
# which sets root permissions on the image
sudo gunzip --force deploy/${device_type}-${artifact_name}.sdimg.gz
fi
cd ${WORKSPACE}/mender-image-tests
# This is a trick to make pytest generate different junit reports
# for different runs: renaming the tests folder to tests_<testsuite>
cp -r tests tests_${device_type}_${artifact_name}
python3 -m pytest --verbose \
--junit-xml="${MENDER_CONVERT_DIR}/results_${device_type}.xml" \
${html_report_args} \
--test-conversion \
--test-variables="${MENDER_CONVERT_DIR}/deploy/${device_type}-${artifact_name}.cfg" \
--board-type="${device_type}" \
--mender-image=${device_type}-${artifact_name}.sdimg \
--sdimg-location="${MENDER_CONVERT_DIR}/deploy" \
tests_${device_type}_${artifact_name} \
${pytest_args_extra}
exitcode=$?
cd -
return $exitcode
}
get_pytest_files() {
wget -N ${MENDER_ACCEPTANCE_URL}/pytest.ini -P $WORKSPACE/mender-image-tests
wget -N ${MENDER_ACCEPTANCE_URL}/common.py -P $WORKSPACE/mender-image-tests
wget -N ${MENDER_ACCEPTANCE_URL}/conftest.py -P $WORKSPACE/mender-image-tests
wget -N ${MENDER_ACCEPTANCE_URL}/fixtures.py -P $WORKSPACE/mender-image-tests
}

1
tests/mender-image-tests

@ -0,0 +1 @@
Subproject commit cc96d0f804be64dd834fbffffafddae50487907e
Loading…
Cancel
Save