Browse Source

Merge pull request #116 from mirzak/master-replaced-v1-with-v2

Master replaced v1 with v2
2.0.x
Mirza Krak 5 years ago
committed by GitHub
parent
commit
844bd49a47
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .dockerignore
  2. 10
      .gitignore
  3. 104
      Dockerfile
  4. 155
      README.md
  5. 44
      bbb-convert-stage-2.sh
  6. 37
      configs/beaglebone_black_base_config
  7. 6
      configs/beaglebone_black_debian_sdcard_config
  8. 147
      configs/mender_convert_config
  9. 35
      configs/mender_grub_config
  10. 16
      configs/qemux86-64_config
  11. 8
      configs/raspberrypi3_config
  12. 82
      configs/raspberrypi_config
  13. 25
      configs/rockpro64_config
  14. 8
      configs/rockpro64_emmc_config
  15. 6
      configs/rockpro64_sd_config
  16. 22
      convert-stage-3.sh
  17. 366
      convert-stage-4.sh
  18. 389
      convert-stage-5.sh
  19. 1
      device-image-shell/.dockerignore
  20. 13
      device-image-shell/Dockerfile
  21. 64
      device-image-shell/README.md
  22. 7
      device-image-shell/docker-build
  23. 58
      device-image-shell/docker-device-image-shell
  24. 38
      device-image-shell/docker-entrypoint.sh
  25. 32
      docker-build
  26. 14
      docker-entrypoint.sh
  27. 15
      docker-mender-convert
  28. 112
      files/init_resize.sh
  29. 13
      files/resizefs.service
  30. 42
      files/resizefs.sh
  31. BIN
      files/uboot_debian_9.4/MLO
  32. BIN
      files/uboot_debian_9.4/u-boot.img
  33. 31
      files/variables.template
  34. 899
      mender-convert
  35. 99
      mender-convert-extract
  36. 888
      mender-convert-functions.sh
  37. 215
      mender-convert-modify
  38. 259
      mender-convert-package
  39. 52
      modules/bootstrap.sh
  40. 20
      modules/config.sh
  41. 176
      modules/disk.sh
  42. 67
      modules/log.sh
  43. 237
      modules/probe.sh
  44. 28
      modules/run.sh
  45. 21
      qemux86_64-convert-stage-2.sh
  46. 1
      requirements-deb.txt
  47. 22
      rockpro64-convert-stage-2.sh
  48. 0
      rootfs_overlay_demo/.gitkeep
  49. 14
      rpi-convert-stage-2.sh
  50. 258
      rpi-convert-stage-5.sh
  51. 13
      scripts/README-run-tests.md
  52. 64
      scripts/bootstrap-rootfs-overlay-demo.sh
  53. 143
      scripts/run-tests.sh

6
.dockerignore

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

10
.gitignore

@ -1,5 +1,5 @@
input/
output/
device-image-shell/output
mendertesting/
integration/
deploy
input
work
rootfs_overlay_demo/*
tests

104
Dockerfile

@ -1,100 +1,40 @@
FROM ubuntu:18.04
FROM ubuntu:19.04
ARG MENDER_ARTIFACT_VERSION=3.0.1
ARG GOLANG_VERSION=1.11.2
ARG MENDER_ARTIFACT_VERSION=3.1.0
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
# 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
# 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
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

155
README.md

@ -1,124 +1,129 @@
[![Build Status](https://travis-ci.com/mendersoftware/mender-convert.svg?branch=master)](https://travis-ci.com/mendersoftware/mender-convert)
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.
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://mender.io/user/pages/resources/06.digital-assets/mender.io.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.
You need to [install Docker Engine](https://docs.docker.com/install) to use this environment.
Download the raw Raspberry Pi disk image into a subdirectory input:
```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
```
### Build the mender-convert container image
Extract the raw Raspberry Pi disk image:
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.
```bash
unzip 2019-06-20-raspbian-buster-lite.zip && cd ..
```
Then run
Bootstrap the demo rootfs overlay that is configured to connect to
https://hosted.mender.io with polling intervals set appropriately for
demonstration purposes:
```bash
./docker-build
```
./scripts/bootstrap-rootfs-overlay-demo.sh \
--output-dir ${PWD}/rootfs_overlay_demo \
--tenant-token "Paste token from Hosted Mender"
```
This will create a container image you can use to run mender-convert.
### Docker environment for mender-convert
### Use the mender-convert container image
To make using mender-convert easier, a reference setup using a Docker
container is provided.
Create a directory `input` under the directory where you copied these files (`docker-build`, `docker-mender-convert`, etc.):
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 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/2019-04-08-raspbian-stretch-lite.img \
--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!
```
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/2019-04-08-raspbian-stretch-lite.img \
--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 (at least to some degree).
## 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 +132,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 +142,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

37
configs/beaglebone_black_base_config

@ -0,0 +1,37 @@
# 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
# 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"
}

6
configs/beaglebone_black_debian_sdcard_config

@ -0,0 +1,6 @@
# 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-2018.07.002.tar"
source configs/beaglebone_black_base_config

147
configs/mender_convert_config

@ -0,0 +1,147 @@
# 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 using gzip
#
# 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.
MENDER_COMPRESS_DISK_IMAGE=y
# 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="2.0.1"
# 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"
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"

16
configs/qemux86-64_config

@ -0,0 +1,16 @@
# 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"

8
configs/raspberrypi3_config

@ -0,0 +1,8 @@
# Binaries generated with the following script:
#
# https://d1b0l86ne08fsf.cloudfront.net/mender-convert/raspberrypi/raspberrypi-integration-scripts.tar.gz
RASPBERRYPI_BINARIES="raspberrypi3-integration-2018.07.tar.gz"
RASPBERRYPI_KERNEL_IMAGE="kernel7.img"
MENDER_KERNEL_IMAGETYPE=zImage
source configs/raspberrypi_config

82
configs/raspberrypi_config

@ -0,0 +1,82 @@
# Raspberry Pi does not support GRUB bootloader integration, fallback to U-boot.
MENDER_GRUB_EFI_INTEGRATION=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/"
# Enable Full KMS (Kernel Mode Setting) graphics driver. This is the
# the open-source GPU driver that is part of mesa, also known as VC4.
#
# This is done because it seems that the firmware GPU driver does currently
# not play well when U-boot is enabled and HDMI output is "scrambled"
#
# Fixes: https://tracker.mender.io/browse/MEN-2685
#
# Will need to revisit this later when there are new firware releases.
#
# Should be noted that RPi4 only works with this enabled and the
# configuration in the Yocto BSP (meta-raspberrypi) is also nowdays using
# this driver by default.
run_and_log_cmd "echo 'dtoverlay=vc4-kms-v3d' | sudo tee -a work/boot/config.txt"
# 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"
# 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_KERNEL_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/"
# 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.
run_and_log_cmd "sudo unlink work/rootfs/etc/rc3.d/S01resize2fs_once"
run_and_log_cmd "sudo rm work/rootfs/etc/init.d/resize2fs_once"
}

25
configs/rockpro64_config

@ -0,0 +1,25 @@
# ROCKPro64 do not support GRUB bootloader integration, fallback to U-boot.
MENDER_GRUB_EFI_INTEGRATION=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.0.1
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=mender-convert
MENDER_CLIENT_VERSION="2.0.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}

14
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

15
docker-mender-convert

@ -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
@ -13,4 +27,5 @@ docker run \
--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=""

899
mender-convert

@ -1,9 +1,24 @@
#!/bin/bash
show_help() {
cat << EOF
Mender conversion tool
#!/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-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"
function show_version() {
echo "Version: ${MENDER_CONVERT_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
function trap_exit() {
sudo rm -rf work
}
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
function trap_term() {
echo "Program interrupted by user"
}
do_install_mender_to_mender_disk_image() {
log "$step/$total Installing Mender to Mender disk image..."
((step++))
trap trap_term INT TERM
trap trap_exit EXIT
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
}
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
}
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
}
PARAMS=""
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
# Load necessary functions.
source ${tool_dir}/mender-convert-functions.sh
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
export -f create_device_maps
export -f detach_device_maps
export -f mount_mender_disk
export -f log
export -f logsetup
source modules/bootstrap.sh
# 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
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
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
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 deploy directory:"
ls -1 deploy/*

99
mender-convert-extract

@ -0,0 +1,99 @@
#!/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.
echo "Running $(basename $0): $@"
source modules/bootstrap.sh
source modules/disk.sh
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
source modules/config.sh "${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 succesfully"
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. Unfortunatly 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.
disk_extract_part "${disk_image}" \
1 $(( $(disk_get_part_value ${disk_image} 1 START) - 1)) work/boot-gap.bin

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
}

215
mender-convert-modify

@ -0,0 +1,215 @@
#!/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.
# Default that can be overridden by providing this method in a
# configuration file passed with '--config'
function platform_modify() {
true
}
function trap_exit() {
echo "mender-convert-modify has finished. Cleaning..."
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
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
source modules/config.sh "${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-convert/deb/${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.service \
work/rootfs/etc/systemd/system/multi-user.target.wants/mender.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
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

259
mender-convert-package

@ -0,0 +1,259 @@
#!/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.
# 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..."
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
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
source modules/config.sh "${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 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 --apparent-size -s --block-size=512 work/rootfs | cut -f 1)
# 50 % free space, not to be confused with rootfs_part_sectors
rootfs_image_sectors=$(awk -v r1="$actual_rootfs_size" 'BEGIN{printf "%.0f", r1 * 1.50}')
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"
fi
# 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 $(disk_sectors_to_mb ${rootfs_part_sectors}) x 2 MiB"
log_info " Data $(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"
# 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
if [ "${MENDER_COMPRESS_DISK_IMAGE}" == "y" ]; then
log_info "Compressing ${sdimg_path}.gz"
run_and_log_cmd "pigz --best --force ${sdimg_path}"
fi
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
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}"
LAYER_CONF_VERSION="2"
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

20
modules/config.sh

@ -0,0 +1,20 @@
#!/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.
for config in configs/mender_convert_config "$@"; 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
}

237
modules/probe.sh

@ -0,0 +1,237 @@
#!/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 "Unfortunatly we where not able to find the Linux kernel image."
log_fatal "Please specifc 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 "Unfortunatly we where not able to find the initrd image."
log_info "Please specifc 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="884893e54a"
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

13
scripts/README-run-tests.md

@ -0,0 +1,13 @@
# Run tests
Run the following commands to install test dependencies (assumes that all mender-convert dependencies are already installed):
sudo apt-get update e2fsprogs=1.44.1-1
sudo apt-get -qy --force-yes install python-pip
sudo pip2 install pytest --upgrade
sudo pip2 install pytest-xdist --upgrade
sudo pip2 install pytest-html --upgrade
Run tests:
./scripts/run-tests.sh

64
scripts/bootstrap-rootfs-overlay-demo.sh

@ -0,0 +1,64 @@
#!/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
# Do not actually paste it here, this is just the default value that will
# end up in mender.conf if no token is specified using '--tenant-token'
tenant_token="Paste your Hosted Mender token here"
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) --tenant-token"
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
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 written to: ${output_dir}/etc/mender"

143
scripts/run-tests.sh

@ -0,0 +1,143 @@
#!/bin/bash
set -e
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
# Relative to where scripts are executed (${WORKSPACE}/mender-image-tests)
MENDER_CONVERT_DIR=../../
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-04-08-raspbian-stretch-lite"
RASPBIAN_IMAGE_URL="https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-04-09/${RASPBIAN_IMAGE}.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"
MENDER_ACCEPTANCE_URL="https://raw.githubusercontent.com/mendersoftware/meta-mender/master/tests/acceptance"
# Some distros do not have /sbin in path for "normal users"
export PATH="${PATH}:/sbin"
convert_and_test() {
device_type=$1
artifact_name=$2
image_url=$3
image_name=$4
image_name_compressed=$5
config=$6 # Optional
wget -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
if [ -n "${config}" ]; then
cp ${config} ${WORKSPACE}/test_config
fi
# Disable compression of disk image when testing, otherwise we need to
# unpack each image we test which is time consuming
echo "MENDER_COMPRESS_DISK_IMAGE=n" >> ${WORKSPACE}/test_config
echo "Configuration used:"
cat ${WORKSPACE}/test_config
MENDER_ARTIFACT_NAME=${artifact_name} ./docker-mender-convert \
--disk-image input/${image_name} \
--config ${WORKSPACE}/test_config
cd ${WORKSPACE}/mender-image-tests
py.test --verbose \
--junit-xml="${WORKSPACE}/results.xml" \
--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"
cd -
}
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
}
if [ ! -d ${WORKSPACE}/mender-image-tests ]; then
git clone https://github.com/mendersoftware/mender-image-tests ${WORKSPACE}/mender-image-tests
else
cd ${WORKSPACE}/mender-image-tests
git pull
cd -
fi
if ! [ -x "$(command -v mender-artifact)" ]; then
echo "mender-artifact: not found in PATH."
github_PR_status "failure" "mender-artifact: not found in PATH."
exit 1
fi
mkdir -p ${WORKSPACE}
get_pytest_files
./docker-build
convert_and_test "qemux86_64" \
"release-1" \
"${UBUNTU_IMAGE_URL}" \
"${UBUNTU_IMAGE}" \
"${UBUNTU_IMAGE}.gz" \
"configs/qemux86-64_config"
convert_and_test "raspberrypi" \
"release-1" \
"${RASPBIAN_IMAGE_URL}" \
"${RASPBIAN_IMAGE}.img" \
"${RASPBIAN_IMAGE}.zip" \
"configs/raspberrypi3_config"
convert_and_test "linaro-alip" \
"release-1" \
"${TINKER_IMAGE_URL}" \
"${TINKER_IMAGE}.img" \
"${TINKER_IMAGE}.zip"
convert_and_test "beaglebone" \
"release-1" \
"${BBB_DEBIAN_IMAGE_URL}" \
"${BBB_DEBIAN_IMAGE}" \
"${BBB_DEBIAN_IMAGE}.xz"
Loading…
Cancel
Save