Browse Source
mender-conversion-tool with add-on scripts allow user to: - shrink an existing embedded Linux image (shorter time of building final .sdimg raw disk image) - restructure partition table of an existing embedded Linux image to follow Mender layout - install Mender client related files - install Grub related files for platforms supporting it - patch U-Boot for platforms where Grub integration is not feasible (e.g. Raspbian) - create Mender artifact based on the created .sdimg file Issues: MEN-1867 / MEN-2037 / MEN-2038 / MEN-2041 Signed-off-by: Adam Podogrocki <a.podogrocki@gmail.com>1.0.x
apodogrocki
7 years ago
committed by
Adam Podogrocki
10 changed files with 2243 additions and 0 deletions
@ -0,0 +1,63 @@ |
|||
[![Build Status](https://travis-ci.org/mendersoftware/mender-crossbuild.svg?branch=master)](https://travis-ci.org/mendersoftware/mender-crossbuild) |
|||
[![codecov](https://codecov.io/gh/mendersoftware/mender-crossbuild/branch/master/graph/badge.svg)](https://codecov.io/gh/mendersoftware/mender-crossbuild) |
|||
|
|||
Mender: over-the-air updater for embedded Linux devices |
|||
============================================== |
|||
|
|||
Mender is an open source over-the-air (OTA) software updater for embedded Linux |
|||
devices. Mender comprises a client running at the embedded device, as well as |
|||
a server that manages deployments across many devices. |
|||
|
|||
Embedded product teams often end up creating homegrown updaters at the last |
|||
minute due to the need to fix bugs in field-deployed devices. However, the most |
|||
important requirement for an embedded update process is *robustness*, for example |
|||
loss of power at any time should not brick a device. This creates a challenge |
|||
given the time constraints to develop and maintain a homegrown updater. |
|||
|
|||
Mender aims to address this challenge with a *robust* and *easy to use* updater |
|||
for embedded Linux devices, which is open source and available to anyone. |
|||
|
|||
Robustness is ensured with *atomic* image-based deployments using a dual A/B |
|||
rootfs partition layout. This makes it always possible to roll back to a working state, even |
|||
when losing power at any time during the update process. |
|||
|
|||
Ease of use is addressed with an intuitive UI, [comprehensive documentation](https://docs.mender.io/), a |
|||
[meta layer for the Yocto Project](https://github.com/mendersoftware/meta-mender) for *easy integration into existing environments*, |
|||
and high quality software (see the test coverage badge). |
|||
|
|||
This repository contains the Mender client updater, which can be run in standalone mode |
|||
(manually triggered through its command line interface) or managed mode (connected to the Mender server). |
|||
|
|||
Mender not only provides the client-side updater, but also the backend and UI |
|||
for managing deployments as open source. The Mender server is |
|||
designed as a microservices architecture and comprises several repositories. |
|||
|
|||
|
|||
## Generic conversion tool |
|||
|
|||
A tool for taking an existing embedded image (Debian, Ubuntu, Raspbian, etc) and converting it to a Mender image by restructuring partition table and adding necessary files. |
|||
|
|||
Since we are unlikely to be able to patch U-Boot this way, this depends on U-Boot/UEFI functionality. |
|||
|
|||
## 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). |
|||
|
|||
## 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. |
|||
|
|||
## Security disclosure |
|||
|
|||
We take security very seriously. If you come across any issue regarding |
|||
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 our [Google group](https://groups.google.com/a/lists.mender.io/forum/#!forum/mender) |
|||
* Follow us on [Twitter](https://twitter.com/mender_io?target=_blank). Please |
|||
feel free to tweet us questions. |
|||
* Fork us on [Github](https://github.com/mendersoftware) |
|||
* Email us at [contact@mender.io](mailto:contact@mender.io) |
@ -0,0 +1,50 @@ |
|||
#!/bin/bash |
|||
|
|||
sdimg_boot_dir=$1 |
|||
embedded_rootfs_dir=$2 |
|||
uboot_backup_dir=${embedded_rootfs_dir}/opt/backup/uboot |
|||
|
|||
echo $sdimg_boot_dir |
|||
echo $embedded_rootfs_dir |
|||
|
|||
[ ! -d "${sdimg_boot_dir}" ] && \ |
|||
{ echo "Error: boot location not mounted."; exit 1; } |
|||
[ ! -d "${embedded_rootfs_dir}" ] && \ |
|||
{ echo "Error: embedded content not mounted."; exit 1; } |
|||
[[ ! -f $uboot_backup_dir/MLO || ! -f $uboot_backup_dir/u-boot.img ]] && \ |
|||
{ echo "Error: cannot find U-Boot related files."; exit 1; } |
|||
|
|||
set_uenv() { |
|||
cat <<- 'EOF' | sudo tee --append $sdimg_boot_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 |
|||
} |
|||
|
|||
## dd if=MLO of=${sdimg_file} count=1 seek=1 bs=128k |
|||
## dd if=u-boot.img of=${sdimg_file} count=2 seek=1 bs=384k |
|||
# Copy U-Boot related files. |
|||
sudo cp ${uboot_backup_dir}/MLO ${sdimg_boot_dir} |
|||
sudo cp ${uboot_backup_dir}/u-boot.img ${sdimg_boot_dir} |
|||
|
|||
# Create U-Boot purposed uEnv.txt file. |
|||
set_uenv |
|||
|
|||
echo -e "\nStage done." |
|||
|
|||
exit 0 |
@ -0,0 +1,20 @@ |
|||
#!/bin/bash |
|||
|
|||
sdimg_primary_dir=$1 |
|||
embedded_rootfs_dir=$2 |
|||
|
|||
[ ! -d "${sdimg_primary_dir}" ] && \ |
|||
{ echo "Error: rootfs location not mounted."; exit 1; } |
|||
[ ! -d "${embedded_rootfs_dir}" ] && \ |
|||
{ echo "Error: embedded content not mounted."; exit 1; } |
|||
|
|||
# Copy rootfs partition. |
|||
sudo cp -ar ${embedded_rootfs_dir}/* ${sdimg_primary_dir} |
|||
|
|||
# Add mountpoints. |
|||
sudo install -d -m 755 ${sdimg_primary_dir}/boot/efi |
|||
sudo install -d -m 755 ${sdimg_primary_dir}/data |
|||
|
|||
echo -e "\nStage done." |
|||
|
|||
exit 0 |
@ -0,0 +1,304 @@ |
|||
#!/bin/bash |
|||
|
|||
show_help() { |
|||
cat << EOF |
|||
|
|||
Tool adding GRUB specific files to Mender compliant .sdimg image file. |
|||
|
|||
Usage: $0 [options] |
|||
|
|||
Options: [-i|--image | -t|--toolchain -k|--keep | -d|--device-type] |
|||
|
|||
--image - .sdimg image generated with mender-conversion-tool |
|||
--toolchain - ARM specific toolchain path |
|||
--keep - prevent deleting GRUB workspace |
|||
--device-type - target device type identification |
|||
|
|||
Note: supported device types are: beaglebone, raspberrypi3 |
|||
|
|||
Examples: |
|||
|
|||
./mender-conversion-tool.sh install_bootloader --image <sdimg_file_path> |
|||
--device-type beaglebone --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 |
|||
grubenv_dir=$output_dir/grubenv |
|||
grubenv_build_dir=$output_dir/grubenv_build |
|||
image= |
|||
toolchain= |
|||
device_type= |
|||
keep=0 |
|||
efi_boot=EFI/BOOT |
|||
EFI_STUB_VER="4.12.0" |
|||
|
|||
declare -a sdimgmappings |
|||
|
|||
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 |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - linux kernel version |
|||
build_env_lock_boot_files() { |
|||
mkdir -p $grubenv_dir |
|||
mkdir -p $grubenv_build_dir |
|||
|
|||
git clone https://github.com/mendersoftware/grub-mender-grubenv.git $grubenv_dir |
|||
cd $grubenv_dir |
|||
|
|||
# Prepare configuration file. |
|||
cp mender_grubenv_defines.example mender_grubenv_defines |
|||
|
|||
local uname_r=$1 |
|||
local kernel_imagetype=vmlinuz-${uname_r} |
|||
local kernel_devicetree=dtbs/${uname_r}/am335x-boneblack.dtb |
|||
|
|||
sed -i '/^kernel_imagetype/s/=.*$/='${kernel_imagetype}'/' mender_grubenv_defines |
|||
sed -i '/^kernel_devicetree/s/=.*$/='${kernel_devicetree//\//\\/}'/' mender_grubenv_defines |
|||
|
|||
make |
|||
make DESTDIR=$grubenv_build_dir install |
|||
rm -rf $grubenv_dir |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - linux kernel version |
|||
build_grub_efi() { |
|||
local grub_arm=$output_dir/grub/arm |
|||
local grub_linux=$output_dir/grub/linux |
|||
local grub_repo_vc_dir=$output_dir/grub/.git |
|||
local repo_clean=0 |
|||
|
|||
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 $output_dir/grub |
|||
local repo_clean=1 |
|||
fi |
|||
|
|||
cd $output_dir/grub/ |
|||
[[ repo_clean -eq 0 ]] && { make --quiet clean; make --quiet distclean; } |
|||
|
|||
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 |
|||
fi |
|||
|
|||
mkdir -p $grub_arm |
|||
mkdir -p $grub_linux |
|||
|
|||
# First build linux tools. |
|||
./autogen.sh |
|||
./configure --quiet CC=gcc --target=x86_64 --with-platform=efi --prefix=$grub_linux |
|||
make --quiet |
|||
make --quiet install |
|||
|
|||
# Clean workspace. |
|||
make --quiet clean |
|||
make --quiet distclean |
|||
|
|||
# Now build ARM modules. |
|||
./configure --host=$toolchain --with-platform=efi --prefix=$grub_arm \ |
|||
CFLAGS="-Os -march=armv7-a" CCASFLAGS="-march=armv7-a" --disable-werror |
|||
make --quiet |
|||
make --quiet install |
|||
|
|||
# Build grub.efi binary. |
|||
$grub_linux/bin/grub-mkimage -v -p /$efi_boot -o grub.efi --format=arm-efi \ |
|||
-d $grub_arm/lib/grub/arm-efi/ 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 |
|||
|
|||
rc=$? |
|||
|
|||
[[ $rc -ne 0 ]] && { return 1; } || { return 0; } |
|||
} |
|||
|
|||
# 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.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 |
|||
install_files() { |
|||
local grubenv_dir=$grubenv_build_dir/boot/efi/EFI/BOOT/ |
|||
local boot_dir=$1 |
|||
local rootfs_dir=$2 |
|||
local efi_boot_dir=$boot_dir/$efi_boot |
|||
|
|||
# Make sure env, lock, lock.sha256sum files exists in working directory. |
|||
[[ ! -d $grubenv_dir/mender_grubenv1 || ! -d $grubenv_dir/mender_grubenv2 ]] && \ |
|||
{ echo "Error: cannot find mender grub related files."; exit 1; } |
|||
|
|||
sudo install -d -m 755 $efi_boot_dir |
|||
|
|||
cd $grubenv_dir && find . -type f -exec sudo install -Dm 644 "{}" "$efi_boot_dir/{}" \; |
|||
cd ${output_dir} |
|||
|
|||
sudo install -m 0644 ${output_dir}/grub/grub.efi $efi_boot_dir |
|||
sudo install -m 0755 ${output_dir}/grub/arm/bin/grub-editenv $rootfs_dir/usr/bin |
|||
|
|||
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 |
|||
|
|||
set_uenv $boot_dir |
|||
} |
|||
|
|||
do_install_bootloader() { |
|||
echo "Setting bootloader..." |
|||
|
|||
if [ -z "${image}" ]; then |
|||
echo ".sdimg image file not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "${toolchain}" ]; then |
|||
echo "ARM toolchain not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "${device_type}" ]; then |
|||
echo "Target device type name not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [[ $(which ${toolchain}-gcc) = 1 ]]; then |
|||
echo "Error: ARM GCC not found in PATH. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
[ ! -f $image ] && { echo "$image - file not found. Aborting."; exit 1; } |
|||
|
|||
# Map & mount Mender compliant image. |
|||
create_device_maps $image sdimgmappings |
|||
|
|||
mkdir -p $output_dir && cd $output_dir |
|||
|
|||
boot=${sdimgmappings[0]} |
|||
primary=${sdimgmappings[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 |
|||
echo -e "\nKernel version: $kernel_version" |
|||
|
|||
build_env_lock_boot_files $kernel_version |
|||
|
|||
build_grub_efi $kernel_version |
|||
rc=$? |
|||
|
|||
[[ $rc -ne 0 ]] && { echo "Error: grub.efi building failure. Aborting."; } \ |
|||
|| { echo "Successful grub.efi building."; \ |
|||
install_files ${path_boot} ${path_primary}; } |
|||
|
|||
# Back to working directory. |
|||
cd $tool_dir && sync |
|||
|
|||
detach_device_maps ${sdimgmappings[@]} |
|||
# Clean files. |
|||
rm -rf $output_dir/sdimg |
|||
|
|||
# [[ $keep -eq 0 ]] && { rm -rf $output_dir; } |
|||
echo -e "\nAll done." |
|||
} |
|||
|
|||
PARAMS="" |
|||
|
|||
while (( "$#" )); do |
|||
case "$1" in |
|||
-i | --image) |
|||
image=$2 |
|||
shift 2 |
|||
;; |
|||
-t | --toolchain) |
|||
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 |
|||
;; |
|||
-*) |
|||
echo "Error: unsupported option $1" >&2 |
|||
exit 1 |
|||
;; |
|||
*) |
|||
PARAMS="$PARAMS $1" |
|||
shift |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
eval set -- "$PARAMS" |
|||
|
|||
# Some commands expect elevated privileges. |
|||
sudo true |
|||
|
|||
do_install_bootloader |
@ -0,0 +1,351 @@ |
|||
#!/bin/bash |
|||
|
|||
show_help() { |
|||
cat << EOF |
|||
|
|||
Mender executables, service and configuration files installer. |
|||
|
|||
Usage: $0 [options] |
|||
|
|||
Options: [-i|--image | -m|--mender | -a|--artifact | -d|--device-type | |
|||
-p|--demo-ip | -u| --production-url | -o| --hosted-token] |
|||
|
|||
--image - .sdimg generated with mender-conversion-tool |
|||
--mender - mender client binary file |
|||
--artifact - artifact info |
|||
--device-type - target device type identification |
|||
--demo-ip - Mender demo server IP address |
|||
--production-url - Mender production server url |
|||
--certificate - Mender server certificate |
|||
|
|||
Examples: |
|||
|
|||
./mender-conversion-tool.sh install_mender --image <sdimg_file_path> |
|||
--device-type beaglebone --artifact release-1_1.5.0 |
|||
--server 192.168.10.2 --mender <mender_binary_path> |
|||
|
|||
EOF |
|||
exit 1 |
|||
} |
|||
|
|||
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" |
|||
output_dir=${tool_dir}/output |
|||
|
|||
mender_client_repo="https://raw.githubusercontent.com/mendersoftware/mender" |
|||
mender_client_revision="1.6.x" |
|||
meta_mender_repo="https://raw.githubusercontent.com/mendersoftware/meta-mender" |
|||
meta_mender_revision="sumo" |
|||
|
|||
mender_dir=$output_dir/mender |
|||
device_type= |
|||
artifact= |
|||
# Mender demo server IP address. |
|||
demo_ip= |
|||
# Mender production server url. |
|||
production_url= |
|||
# Used server url. |
|||
server_url="https://docker.mender.io" |
|||
# Mender production certificate. |
|||
certificate= |
|||
# Mender hosted token. |
|||
hosted_token= |
|||
# Mender tenant token. |
|||
tenant_token="dummy" |
|||
|
|||
declare -a sdimgmappings |
|||
|
|||
create_client_files() { |
|||
cat <<- EOF > $mender_dir/mender.service |
|||
[Unit] |
|||
Description=Mender OTA update service |
|||
After=systemd-resolved.service |
|||
|
|||
[Service] |
|||
Type=idle |
|||
User=root |
|||
Group=root |
|||
ExecStartPre=/bin/mkdir -p -m 0700 /data/mender |
|||
ExecStartPre=/bin/ln -sf /etc/mender/tenant.conf /var/lib/mender/authtentoken |
|||
ExecStart=/usr/bin/mender -daemon |
|||
Restart=on-abort |
|||
|
|||
[Install] |
|||
WantedBy=multi-user.target |
|||
EOF |
|||
|
|||
cat <<- EOF > $mender_dir/mender.conf |
|||
{ |
|||
"InventoryPollIntervalSeconds": 5, |
|||
"RetryPollIntervalSeconds": 1, |
|||
"RootfsPartA": "/dev/mmcblk0p2", |
|||
"RootfsPartB": "/dev/mmcblk0p3", |
|||
"ServerCertificate": "/etc/mender/server.crt", |
|||
"ServerURL": "$server_url", |
|||
"TenantToken": "$tenant_token", |
|||
"UpdatePollIntervalSeconds": 5 |
|||
} |
|||
EOF |
|||
|
|||
cat <<- EOF > $mender_dir/artifact_info |
|||
artifact_name=${artifact} |
|||
EOF |
|||
|
|||
# Version file |
|||
echo -n "2" > $mender_dir/version |
|||
|
|||
cat <<- EOF > $mender_dir/device_type |
|||
device_type=${device_type} |
|||
EOF |
|||
|
|||
case "$device_type" in |
|||
"beaglebone") |
|||
cat <<- EOF > $mender_dir/fw_env.config |
|||
/dev/mmcblk0 0x800000 0x20000 |
|||
/dev/mmcblk0 0x1000000 0x20000 |
|||
EOF |
|||
;; |
|||
"raspberrypi3") |
|||
cat <<- EOF > $mender_dir/fw_env.config |
|||
/dev/mmcblk0 0x400000 0x4000 |
|||
/dev/mmcblk0 0x800000 0x4000 |
|||
EOF |
|||
;; |
|||
esac |
|||
} |
|||
|
|||
get_mender_files_from_upstream() { |
|||
mkdir -p $mender_dir |
|||
|
|||
echo -e "Downloading inventory & identity scripts..." |
|||
|
|||
wget -q -O $mender_dir/mender-device-identity \ |
|||
$mender_client_repo/$mender_client_revision/support/mender-device-identity |
|||
wget -q -O $mender_dir/mender-inventory-bootloader-integration \ |
|||
$mender_client_repo/$mender_client_revision/support/mender-inventory-bootloader-integration |
|||
wget -q -O $mender_dir/mender-inventory-hostinfo \ |
|||
$mender_client_repo/$mender_client_revision/support/mender-inventory-hostinfo |
|||
wget -q -O $mender_dir/mender-inventory-network \ |
|||
$mender_client_repo/$mender_client_revision/support/mender-inventory-network |
|||
wget -q -O $mender_dir/mender-inventory-os \ |
|||
$mender_client_repo/$mender_client_revision/support/mender-inventory-os |
|||
wget -q -O $mender_dir/mender-inventory-rootfs-type \ |
|||
$mender_client_repo/$mender_client_revision/support/mender-inventory-rootfs-type |
|||
wget -q -O $mender_dir/server.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 |
|||
|
|||
identitydir="usr/share/mender/identity" |
|||
inventorydir="usr/share/mender/inventory" |
|||
sysconfdir="etc/mender" |
|||
bindir="usr/bin" |
|||
systemd_unitdir="lib/systemd/system" |
|||
localstatedir="var/lib/mender" |
|||
dataconfdir="mender" |
|||
databootdir="u-boot" |
|||
|
|||
# Prepare 'data' partition |
|||
sudo install -d -m 755 ${data_dir}/${dataconfdir} |
|||
sudo install -d -m 755 ${data_dir}/${databootdir} |
|||
|
|||
sudo install -m 0644 ${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 |
|||
|
|||
# Prepare 'primary' partition |
|||
[ ! -d "$primary_dir/data" ] && \ |
|||
{ echo "'data' mountpoint missing. Adding"; \ |
|||
sudo install -d -m 755 ${primary_dir}/data; } |
|||
|
|||
case "$device_type" in |
|||
"beaglebone") |
|||
[ ! -d "$primary_dir/boot/efi" ] && \ |
|||
{ echo "'/boot/efi' mountpoint missing. Adding"; \ |
|||
sudo install -d -m 755 ${primary_dir}/boot/efi; } |
|||
;; |
|||
"raspberrypi3") |
|||
[ ! -d "$primary_dir/uboot" ] && \ |
|||
{ echo "'/boot/efi' mountpoint missing. Adding"; \ |
|||
sudo install -d -m 755 ${primary_dir}/uboot; } |
|||
;; |
|||
esac |
|||
|
|||
sudo install -d ${primary_dir}/${identitydir} |
|||
sudo install -d ${primary_dir}/${inventorydir} |
|||
sudo install -d ${primary_dir}/${sysconfdir} |
|||
sudo install -d ${primary_dir}/${sysconfdir}/scripts |
|||
|
|||
sudo ln -s /data/${dataconfdir} ${primary_dir}/${localstatedir} |
|||
|
|||
sudo install -m 0755 ${mender} ${primary_dir}/${bindir}/mender |
|||
|
|||
sudo install -t ${primary_dir}/${identitydir} -m 0755 \ |
|||
${mender_dir}/mender-device-identity |
|||
|
|||
sudo install -t ${primary_dir}/${inventorydir} -m 0755 \ |
|||
${mender_dir}/mender-inventory-* |
|||
|
|||
sudo install -m 0644 ${mender_dir}/mender.service ${primary_dir}/${systemd_unitdir} |
|||
|
|||
# Enable menderd service starting on boot. |
|||
sudo ln -s /lib/systemd/system/mender.service \ |
|||
${primary_dir}/etc/systemd/system/multi-user.target.wants/mender.service |
|||
|
|||
sudo install -m 0644 ${mender_dir}/mender.conf ${primary_dir}/${sysconfdir} |
|||
|
|||
sudo install -m 0444 ${mender_dir}/server.crt ${primary_dir}/${sysconfdir} |
|||
|
|||
sudo install -m 0644 ${mender_dir}/artifact_info ${primary_dir}/${sysconfdir} |
|||
|
|||
sudo install -m 0644 ${mender_dir}/version ${primary_dir}/${sysconfdir}/scripts |
|||
|
|||
if [ -n "${demo_ip}" ]; then |
|||
echo "$demo_ip docker.mender.io s3.docker.mender.io" | sudo tee -a $primary_dir/etc/hosts |
|||
fi |
|||
|
|||
if [ -n "${certificate}" ]; then |
|||
sudo install -m 0444 ${certificate} ${primary_dir}/${sysconfdir} |
|||
fi |
|||
} |
|||
|
|||
do_install_mender() { |
|||
if [ -z "${image}" ]; then |
|||
echo ".sdimg image file not set. Aborting." |
|||
show_help |
|||
fi |
|||
|
|||
if [ -z "${mender}" ]; then |
|||
echo "Mender client binary not set. Aborting." |
|||
show_help |
|||
fi |
|||
|
|||
if [ -z "${device_type}" ]; then |
|||
echo "Target device type name not set. Aborting." |
|||
show_help |
|||
fi |
|||
|
|||
if [ -z "${artifact}" ]; then |
|||
echo "Artifact info not set. Aborting." |
|||
show_help |
|||
fi |
|||
|
|||
if [ -z "${production_url}" ] && [ -z "${demo_ip}" ] && \ |
|||
[ -z "${hosted_token}" ]; then |
|||
echo "No server type specified. Aborting." |
|||
show_help |
|||
fi |
|||
|
|||
if [ -n "${production_url}" ] && [ -n "${demo_ip}" ]; then |
|||
echo "Incompatible server type choice. Aborting." |
|||
show_help |
|||
fi |
|||
|
|||
# TODO: more error checking of server types |
|||
if [ -n "${hosted_token}" ]; then |
|||
tenant_token=$(cat ${hosted_token} | tr -d '\n') |
|||
server_url="https://hosted.mender.io" |
|||
fi |
|||
|
|||
if [ -n "${production_url}" ]; then |
|||
server_url=${production_url} |
|||
fi |
|||
|
|||
[ ! -f $image ] && { echo "$image - file not found. Aborting."; exit 1; } |
|||
|
|||
# Mount rootfs partition A. |
|||
create_device_maps $image sdimgmappings |
|||
|
|||
mkdir -p $output_dir && cd $output_dir |
|||
|
|||
primary=${sdimgmappings[1]} |
|||
data=${sdimgmappings[3]} |
|||
|
|||
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 ${sdimgmappings[@]} |
|||
rm -rf $output_dir/sdimg |
|||
} |
|||
|
|||
PARAMS="" |
|||
|
|||
while (( "$#" )); do |
|||
case "$1" in |
|||
-i | --image) |
|||
image=$2 |
|||
shift 2 |
|||
;; |
|||
-m | --mender) |
|||
mender=$2 |
|||
shift 2 |
|||
;; |
|||
-d | --device-type) |
|||
device_type=$2 |
|||
shift 2 |
|||
;; |
|||
-a | --artifact) |
|||
artifact=$2 |
|||
shift 2 |
|||
;; |
|||
-p | --demo-ip) |
|||
demo_ip=$2 |
|||
shift 2 |
|||
;; |
|||
-c | --certificate) |
|||
certificate=$2 |
|||
shift 2 |
|||
;; |
|||
-u | --production-url) |
|||
production_url=$2 |
|||
shift 2 |
|||
;; |
|||
-o | --hosted-token) |
|||
hosted_token=$2 |
|||
shift 2 |
|||
;; |
|||
-h | --help) |
|||
show_help |
|||
;; |
|||
--) |
|||
shift |
|||
break |
|||
;; |
|||
-*) |
|||
echo "Error: unsupported option $1" >&2 |
|||
exit 1 |
|||
;; |
|||
*) |
|||
PARAMS="$PARAMS $1" |
|||
shift |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
eval set -- "$PARAMS" |
|||
|
|||
# Some commands expect elevated privileges. |
|||
sudo true |
|||
|
|||
do_install_mender |
@ -0,0 +1,618 @@ |
|||
#!/bin/bash |
|||
|
|||
# Erase block size 8MiB |
|||
erase_block=8388608 |
|||
heads=255 |
|||
sectors=63 |
|||
|
|||
declare -a sdimg_partitions=("boot" "primary" "secondary" "data") |
|||
declare -a embedded_partitions=("boot" "rootfs") |
|||
|
|||
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" |
|||
output_dir=${tool_dir}/output |
|||
|
|||
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 |
|||
|
|||
# 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} |
|||
;; |
|||
*) |
|||
echo "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 |
|||
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 |
|||
d # delete partition |
|||
2 |
|||
n |
|||
p |
|||
2 |
|||
${rootfsstart} |
|||
+${rootfsstop} |
|||
w # write the partition table |
|||
q # and we're done |
|||
EOF |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - embedded image path |
|||
# |
|||
# Calculates following values: |
|||
# |
|||
# $2 - number of partitions |
|||
# $3 - size of the sector (in bytes) |
|||
# $4 - boot partition start offset (in sectors) |
|||
# $5 - boot partition size (in sectors) |
|||
# $6 - root filesystem partition start offset (in sectors) |
|||
# $7 - root filesystem partition size (in sectors) |
|||
# $8 - boot flag |
|||
get_image_info() { |
|||
local limage=$1 |
|||
local rvar_count=$2 |
|||
local rvar_sectorsize=$3 |
|||
local rvar_bootstart=$4 |
|||
local rvar_bootsize=$5 |
|||
local rvar_rootfsstart=$6 |
|||
local rvar_rootfssize=$7 |
|||
local rvar_bootflag=$8 |
|||
|
|||
local lbootsize=0 |
|||
local lsubname=${limage:0:8} |
|||
local lfdisk="$(fdisk -u -l ${limage})" |
|||
|
|||
local lparts=($(echo "${lfdisk}" | grep "^${lsubname}" | cut -d' ' -f1)) |
|||
local lcount=${#lparts[@]} |
|||
|
|||
local lsectorsize=($(echo "${lfdisk}" | grep '^Sector' | cut -d' ' -f4)) |
|||
|
|||
local lfirstpartinfo="$(echo "${lfdisk}" | grep "^${lparts[0]}")" |
|||
|
|||
idx_start=2 |
|||
idx_size=4 |
|||
|
|||
if [[ $lcount -gt 1 ]]; then |
|||
local lsecondpartinfo="$(echo "${lfdisk}" | grep "^${lparts[1]}")" |
|||
local lsecondpartstart=($(echo "${lsecondpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_start})) |
|||
local lsecondpartsize=($(echo "${lsecondpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_size})) |
|||
fi |
|||
|
|||
eval $rvar_bootflag="0" |
|||
if [[ "$lfirstpartinfo" =~ .*\*.* ]]; then |
|||
eval $rvar_bootflag="1" |
|||
((idx_start+=1)) |
|||
((idx_size+=1)) |
|||
fi |
|||
|
|||
lfirstpartsize=($(echo "${lfirstpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_size})) |
|||
lfirstpartstart=($(echo "${lfirstpartinfo}" | tr -s ' ' | cut -d' ' -f${idx_start})) |
|||
|
|||
eval $rvar_count="'$lcount'" |
|||
eval $rvar_sectorsize="'$lsectorsize'" |
|||
eval $rvar_bootstart="'$lfirstpartstart'" |
|||
eval $rvar_bootsize="'$lfirstpartsize'" |
|||
eval $rvar_rootfsstart="'$lsecondpartstart'" |
|||
eval $rvar_rootfssize="'$lsecondpartsize'" |
|||
|
|||
[[ $lcount -gt 2 ]] && \ |
|||
{ echo "Unsupported type of source image. Aborting."; return 1; } || \ |
|||
{ return 0; } |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw 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_sdimg_info() { |
|||
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:8} |
|||
local lfdisk="$(fdisk -u -l ${limage})" |
|||
|
|||
local lparts=($(echo "${lfdisk}" | grep "^${lsubname}" | cut -d' ' -f1)) |
|||
local lcount=${#lparts[@]} |
|||
|
|||
if [[ $lcount -ne 4 ]]; then |
|||
echo "Error: invalid source .sdimg file. 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 |
|||
# $2 - size of the sector |
|||
# |
|||
align_partition_size() { |
|||
# Final size is aligned to 8MiB. |
|||
local rvar_size=$1 |
|||
local -n ref=$1 |
|||
|
|||
local size_in_bytes=$(( $ref * $2 )) |
|||
local reminder=$(( ${size_in_bytes} % ${erase_block} )) |
|||
|
|||
if [ $reminder -ne 0 ]; then |
|||
size_in_bytes=$(( $size_in_bytes - $reminder + ${erase_block} )) |
|||
fi |
|||
|
|||
local lsize=$(( $size_in_bytes / $2 )) |
|||
|
|||
eval $rvar_size="'$lsize'" |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - embedded image |
|||
# |
|||
# Returns: |
|||
# |
|||
# $2 - boot partition start offset (in sectors) |
|||
# $3 - boot partition size (in sectors) |
|||
# $4 - root filesystem partition size (in sectors) |
|||
# $5 - sector size (in bytes) |
|||
# $6 - image type |
|||
analyse_embedded_image() { |
|||
local image=$1 |
|||
local count= |
|||
local sectorsize= |
|||
local bootstart= |
|||
local bootsize= |
|||
local rootfsstart= |
|||
local rootfssize= |
|||
local bootflag= |
|||
|
|||
local rvar_bootstart=$2 |
|||
local rvar_bootsize=$3 |
|||
local rvar_rootfssize=$4 |
|||
local rvar_sectorsize=$5 |
|||
local rvar_imagetype=$6 |
|||
|
|||
get_image_info $image count sectorsize bootstart bootsize \ |
|||
rootfsstart rootfssize bootflag |
|||
|
|||
[[ $? -ne 0 ]] && \ |
|||
{ echo "Error: invalid/unsupported embedded image. Aborting."; exit 1; } |
|||
|
|||
if [[ $count -eq 1 ]]; then |
|||
echo -e "\nDetected single partition embedded image." |
|||
rootfssize=$bootsize |
|||
# Default size of the boot partition: 16MiB. |
|||
bootsize=$(( ($erase_block * 2) / $sectorsize )) |
|||
elif [[ $count -eq 2 ]]; then |
|||
echo -e "\nDetected multipartition ($count) embedded image." |
|||
fi |
|||
|
|||
align_partition_size bootsize $sectorsize |
|||
align_partition_size rootfssize $sectorsize |
|||
|
|||
eval $rvar_bootstart="'$bootstart'" |
|||
eval $rvar_bootsize="'$bootsize'" |
|||
eval $rvar_rootfssize="'$rootfssize'" |
|||
eval $rvar_sectorsize="'$sectorsize'" |
|||
eval $rvar_imagetype="'$count'" |
|||
|
|||
echo -e "\nEmbedded image processing summary:\ |
|||
\nboot part start sector: ${bootstart}\ |
|||
\nboot part size (sectors): ${bootsize}\ |
|||
\nrootfs part size (sectors): ${rootfssize}\ |
|||
\nsector size (bytes): ${sectorsize}\n" |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - boot partition start offset (in sectors) |
|||
# $2 - boot partition size (in sectors) |
|||
# $3 - root filesystem partition size (in sectors) |
|||
# $4 - data partition size (in MB) |
|||
# $5 - sector size (in bytes) |
|||
# |
|||
# Returns: |
|||
# |
|||
# $6 - aligned data partition size (in sectors) |
|||
# $7 - final .sdimg file size (in bytes) |
|||
calculate_sdimg_size() { |
|||
local rvar_datasize=$6 |
|||
local rvar_sdimgsize=$7 |
|||
|
|||
local datasize=$(( ($4 * 1024 * 1024) / $5 )) |
|||
|
|||
align_partition_size datasize $5 |
|||
|
|||
local sdimgsize=$(( ($1 + $2 + 2 * ${3} + $datasize) * $5 )) |
|||
|
|||
eval $rvar_datasize="'$datasize'" |
|||
eval $rvar_sdimgsize="'$sdimgsize'" |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw disk image |
|||
unmount_partitions() { |
|||
echo "1. Check if device is mounted..." |
|||
is_mounted=`grep ${1} /proc/self/mounts | wc -l` |
|||
if [ ${is_mounted} -ne 0 ]; then |
|||
sudo umount ${1}?* |
|||
fi |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw disk image |
|||
erase_filesystem() { |
|||
echo "2. Erase filesystem..." |
|||
sudo wipefs --all --force ${1}?* |
|||
sudo dd if=/dev/zero of=${1} bs=1M count=100 |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw disk image path |
|||
# $2 - raw disk image size |
|||
create_sdimg() { |
|||
local lfile=$1 |
|||
local lsize=$2 |
|||
local bs=$(( 1024*1024 )) |
|||
local count=$(( ${lsize} / ${bs} )) |
|||
|
|||
echo -e "\nWriting $lsize bytes to .sdimg file..." |
|||
dd if=/dev/zero of=${lfile} bs=${bs} count=${count} |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw disk image path |
|||
# $2 - raw disk image size |
|||
# $3 - boot partition start offset |
|||
# $4 - boot partition size |
|||
# $5 - root filesystem partiotion size |
|||
# $6 - data partition size |
|||
# $7 - sector size |
|||
format_sdimg() { |
|||
local lfile=$1 |
|||
local lsize=$2 |
|||
|
|||
# if [ -z "$3" ]; then |
|||
# echo "Error: no root filesystem size provided" |
|||
# exit 1 |
|||
# fi |
|||
|
|||
# if [ -z "$2" ]; then |
|||
# size=$(sudo blockdev --getsize64 ${sdimg_file}) |
|||
# else |
|||
# size=$2 |
|||
# fi |
|||
|
|||
cylinders=$(( ${lsize} / ${heads} / ${sectors} / ${7} )) |
|||
rootfs_size=$(( $5 - 1 )) |
|||
pboot_offset=$(( ${4} - 1 )) |
|||
primary_start=$(( ${3} + ${pboot_offset} + 1 )) |
|||
secondary_start=$(( ${primary_start} + ${rootfs_size} + 1 )) |
|||
data_start=$(( ${secondary_start} + ${rootfs_size} + 1 )) |
|||
data_offset=$(( ${6} - 1 )) |
|||
|
|||
echo $3 ${pboot_offset} $primary_start $secondary_start $data_start $data_offset |
|||
|
|||
echo -e "\nFormatting .sdimg file..." |
|||
|
|||
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | sudo fdisk ${lfile} |
|||
o # clear the in memory partition table |
|||
x |
|||
h |
|||
${heads} |
|||
s |
|||
${sectors} |
|||
c |
|||
${cylinders} |
|||
r |
|||
n # new partition |
|||
p # primary partition |
|||
1 # partition number 1 |
|||
${3} # default - start at beginning of disk |
|||
+${pboot_offset} # 16 MB boot parttion |
|||
t |
|||
c |
|||
a |
|||
n # new partition |
|||
p # primary partition |
|||
2 # partion number 2 |
|||
${primary_start} # start immediately after preceding partition |
|||
+${rootfs_size} |
|||
n # new partition |
|||
p # primary partition |
|||
3 # partion number 3 |
|||
${secondary_start} # start immediately after preceding partition |
|||
+${rootfs_size} |
|||
n # new partition |
|||
p # primary partition |
|||
${data_start} # start immediately after preceding partition |
|||
+${data_offset} |
|||
p # print the in-memory partition table |
|||
w # write the partition table |
|||
q # and we're done |
|||
EOF |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw disk file |
|||
# |
|||
# Returns: |
|||
# |
|||
# $2 - number of detected partitions |
|||
verify_sdimg() { |
|||
local lfile=$1 |
|||
local rvar_no_of_parts=$2 |
|||
|
|||
local limage=$(basename $lfile) |
|||
local partitions=($(fdisk -l -u ${limage} | grep '^mender' | cut -d' ' -f1)) |
|||
|
|||
local no_of_parts=${#partitions[@]} |
|||
|
|||
[[ $no_of_parts -eq 4 ]] || \ |
|||
{ echo "Error: incorrect number of partitions: $no_of_parts. Aborting."; return 1; } |
|||
|
|||
eval $rvar_no_of_parts=="'$no_of_parts='" |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - raw disk image |
|||
# $2 - partition mappings holder |
|||
create_device_maps() { |
|||
local -n mappings=$2 |
|||
|
|||
if [[ -n "$1" ]]; then |
|||
mapfile -t mappings < <( sudo kpartx -v -a $1 | grep 'loop' | cut -d' ' -f3 ) |
|||
[[ ${#mappings[@]} -eq 0 ]] \ |
|||
&& { echo "Error: partition mappings failed. Aborting."; exit 1; } \ |
|||
|| { echo "Mapped ${#mappings[@]} partition(s)."; } |
|||
else |
|||
echo "Error: no device passed. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
sudo partprobe /dev/${mappings[0]%p*} |
|||
|
|||
echo "Mapper device: ${mappings[0]%p*}" |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - partition mappings holder |
|||
detach_device_maps() { |
|||
local mappings=($@) |
|||
|
|||
[ ${#mappings[@]} -eq 0 ] && { echo "Nothing to detach."; 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_sdimg_filesystem() { |
|||
local mappings=($@) |
|||
echo -e "\nCreating filesystem for ${#mappings[@]} partitions..." |
|||
|
|||
for mapping in ${mappings[@]} |
|||
do |
|||
map_dev=/dev/mapper/"$mapping" |
|||
part_no=$(get_part_number_from_device $map_dev) |
|||
|
|||
echo -e "\nFormatting partition: ${part_no}..." |
|||
|
|||
if [[ part_no -eq 1 ]]; then |
|||
sudo mkfs.vfat -n ${sdimg_partitions[${part_no} - 1]} $map_dev |
|||
else |
|||
sudo mkfs.ext4 -L ${sdimg_partitions[${part_no} - 1]} $map_dev |
|||
fi |
|||
done |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - partition mappings holder |
|||
mount_embedded() { |
|||
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/${embedded_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_sdimg() { |
|||
local mappings=($@) |
|||
|
|||
for mapping in ${mappings[@]} |
|||
do |
|||
local part_no=${mapping#*p*p} |
|||
local path=$sdimg_base_dir/${sdimg_partitions[${part_no} - 1]} |
|||
mkdir -p $path |
|||
sudo mount /dev/mapper/"${mapping}" $path 2>&1 >/dev/null |
|||
done |
|||
} |
|||
|
|||
# Takes following arguments |
|||
# |
|||
# $1 - device type |
|||
set_fstab() { |
|||
echo -e "\nSetting fstab..." |
|||
local mountpoint= |
|||
local device_type=$1 |
|||
local sysconfdir="$sdimg_primary_dir/etc" |
|||
|
|||
[ ! -d "${sysconfdir}" ] && { echo "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" |
|||
;; |
|||
"raspberrypi3") |
|||
mountpoint="/uboot" |
|||
;; |
|||
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/mmcblk0p1 $mountpoint auto defaults,sync 0 0 |
|||
/dev/mmcblk0p4 /data auto defaults 0 0 |
|||
EOF" |
|||
} |
|||
|
|||
# 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() { |
|||
local cmd="dd if=$1 of=${output_dir}/$4 skip=$2 bs=512 count=$3 status=progress" |
|||
|
|||
echo "Running command:" |
|||
echo " ${cmd}" |
|||
$(${cmd}) |
|||
} |
@ -0,0 +1,618 @@ |
|||
#!/bin/bash |
|||
|
|||
show_help() { |
|||
cat << EOF |
|||
|
|||
Mender conversion tool |
|||
|
|||
A tool for taking an existing embedded image (Debian, Ubuntu, Raspbian, etc) and |
|||
convert it to a Mender image by restructuring partition table and adding |
|||
necessary files. |
|||
|
|||
Usage: $0 [prepare_image | make_sdimg | install_mender | install_bootloader | make_all] [options] |
|||
|
|||
Actions: |
|||
|
|||
prepare_image - shrinks existing embedded image |
|||
|
|||
make_sdimg - composes image file compliant with Mender partition |
|||
layout |
|||
|
|||
install_mender - installs Mender client related files |
|||
|
|||
install_bootloader - installs Grub related files |
|||
|
|||
make_artifact - creates Mender artifact file based on .sdimg file |
|||
|
|||
make_all - composes fully functional .sdimg file compliant |
|||
with Mender partition layout, having all necessary |
|||
files installed |
|||
|
|||
Options: [-e|--embedded | -i|--image | -s|--size-data | -d|--device-type | |
|||
-r|--rootfs_type | -p| --demo-ip | -c| --certificate | |
|||
-u| --production-url | -o| --hosted-token] |
|||
|
|||
embedded - raw disk embedded linux image path, e.g. Debian 9.3, Raspbian, etc. |
|||
image - raw disk .sdimg file name where the script writes to |
|||
size-data - size of data partition in MiB; default value 128MiB |
|||
device-type - target device identification used to build .sdimg name |
|||
rootfs_type - selects rootfs_a|rootfs_b as the source filesystem for an artifact |
|||
demo_ip - server demo ip used for testing purposes |
|||
certificate - server certificate file |
|||
production-url - production server url |
|||
hosted-token - mender hosted token |
|||
|
|||
Note: root filesystem size used in .sdimg creation can be found as an |
|||
output from 'prepare_image' command or, in case of using unmodified |
|||
embedded image, can be checked with any partition manipulation |
|||
program (e.g. parted, fdisk, etc.). |
|||
|
|||
Examples: |
|||
|
|||
To shrink the existing embedded image: |
|||
|
|||
./mender-conversion-tool.sh prepare_image --embedded <embedded_image_file_path> |
|||
|
|||
Output: Root filesystem size (sectors): 4521984 |
|||
|
|||
To prepare .sdimg file: |
|||
|
|||
./mender-conversion-tool.sh make_sdimg --image <sdimg_file_name> |
|||
--embedded <embedded_image_file_path> |
|||
--size-data 128 --device-type beaglebone |
|||
|
|||
Output: ready to use ./output/*.sdimg file which can be used to flash SD card |
|||
|
|||
To install Mender client related files: |
|||
|
|||
./mender-conversion-tool.sh install_mender --image <sdimg_file_path> |
|||
--device-type beaglebone --artifact release-1_1.5.0 |
|||
--server 192.168.10.2 --mender <mender_binary_path> |
|||
|
|||
Output: ./output/*.sdimg file with Mender client related files installed |
|||
|
|||
To install Grub/U-Boot related files: |
|||
|
|||
./mender-conversion-tool.sh install_bootloader --image <sdimg_file_path> |
|||
--device-type beaglebone --toolchain arm-linux-gnueabihf |
|||
|
|||
Output: ./output/*.sdimg file with Grub/U-Boot related files installed |
|||
|
|||
To prepare .mender artifact file: |
|||
|
|||
./mender-conversion-tool.sh make_artifact --image <sdimg_file_path> |
|||
--device-type beaglebone --artifact release-1_1.5.0 |
|||
--rootfs-type rootfs_a |
|||
|
|||
Note: artifact name format is: release-<release_no>_<mender_version> |
|||
|
|||
To compose .sdimg file in a single step: |
|||
|
|||
./mender-conversion-tool.sh make_all --embedded <embedded_image_file_path> |
|||
--image <sdimg_file_name> --device-type raspberrypi3 |
|||
--mender <mender_binary_path> --artifact release-1_1.5.0 |
|||
--demo-ip 192.168.10.2 --toolchain arm-linux-gnueabihf --keep |
|||
|
|||
EOF |
|||
} |
|||
|
|||
if [ $# -eq 0 ]; then |
|||
show_help |
|||
exit 1 |
|||
fi |
|||
|
|||
tool_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" |
|||
|
|||
# Default sector size |
|||
sector_size= |
|||
# Boot partition start in sectors (512 bytes per sector). |
|||
pboot_start= |
|||
# Default 'boot' partition size in sectors: 16MiB |
|||
# (i.e 16777216 bytes, equals to 'erase_block' * 2) |
|||
pboot_size= |
|||
# Default 'data' partition size in MiB. |
|||
data_size=128 |
|||
# Data partition size in sectors. |
|||
pdata_size= |
|||
# Exemplary values for Beaglebone: 9.3: 4521984 9.4: 4423680 |
|||
prootfs_size= |
|||
|
|||
menderimage= |
|||
embeddedimage= |
|||
device_type= |
|||
partitions_number= |
|||
artifact= |
|||
rootfs_type= |
|||
image_type= |
|||
mender= |
|||
# Mender production certificate. |
|||
certificate= |
|||
# Mender production server url. |
|||
production_url= |
|||
# Mender demo server IP address. |
|||
demo_ip= |
|||
# Mender hosted token. |
|||
hosted_token= |
|||
|
|||
declare -a rootfs_types=("rootfs_a" "rootfs_b") |
|||
declare -a sdimgmappings |
|||
declare -a embedmappings |
|||
|
|||
do_prepare_image() { |
|||
if [ -z "${embeddedimage}" ]; then |
|||
echo "Embedded image not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
local count= |
|||
local bootstart= |
|||
local bootsize= |
|||
local rootfsstart= |
|||
local rootfssize= |
|||
local bootflag= |
|||
|
|||
# Gather information about embedded image. |
|||
get_image_info $embeddedimage count sector_size bootstart bootsize \ |
|||
rootfsstart rootfssize bootflag |
|||
|
|||
# Find first available loopback device. |
|||
loopdevice=($(losetup -f)) |
|||
|
|||
# Mount appropriate partition. |
|||
if [[ $count -eq 1 ]]; then |
|||
sudo losetup $loopdevice $embeddedimage -o $(($bootstart * $sector_size)) |
|||
elif [[ $count -eq 2 ]]; then |
|||
sudo losetup $loopdevice $embeddedimage -o $(($rootfsstart * $sector_size)) |
|||
else |
|||
echo "Error: invalid/unsupported embedded image. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
[ $? -ne 0 ] && { echo "Error: inaccesible loopback device"; exit 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 |
|||
|
|||
echo -e "Root filesystem size:" |
|||
echo -e "\nminimal: $(( $min_size_blocks * $block_size ))" |
|||
echo -e "\naligned: $(( $new_size_sectors * $sector_size ))" |
|||
echo -e "\nsectors: $new_size_sectors" |
|||
|
|||
sudo e2fsck -y -f $loopdevice |
|||
sudo resize2fs -p $loopdevice ${new_size_sectors}s |
|||
sudo e2fsck -y -f $loopdevice |
|||
|
|||
sudo losetup -d $loopdevice |
|||
sudo losetup $loopdevice $embeddedimage |
|||
|
|||
if [[ $count -eq 1 ]]; then |
|||
create_single_disk_partition_table $loopdevice $bootstart $new_size_sectors |
|||
elif [[ $count -eq 2 ]]; then |
|||
echo |
|||
create_double_disk_partition_table $loopdevice $rootfsstart $new_size_sectors |
|||
fi |
|||
|
|||
sudo partprobe |
|||
endsector=($(sudo parted $loopdevice -ms unit s print | grep "^$count" | cut -f3 -d: | sed 's/[^0-9]*//g')) |
|||
|
|||
sudo losetup -d $loopdevice |
|||
echo "Image new endsector: $endsector" |
|||
truncate -s $((($endsector+1) * $sector_size)) $embeddedimage |
|||
echo "Root filesystem size (sectors): $new_size_sectors" |
|||
} |
|||
|
|||
do_make_sdimg() { |
|||
if [ -z "${embeddedimage}" ]; then |
|||
echo "Source embedded image not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "${device_type}" ]; then |
|||
echo "Target device type name not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [[ ! -f ${embeddedimage} ]]; then |
|||
echo "Source embedded image not found. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
mkdir -p $output_dir && cd $output_dir |
|||
|
|||
# In case of missing .sdimg name use the default format. |
|||
[ -z $menderimage ] && menderimage=$output_dir/mender_${device_type}.sdimg \ |
|||
|| menderimage=$output_dir/$menderimage |
|||
|
|||
analyse_embedded_image ${embeddedimage} pboot_start pboot_size prootfs_size \ |
|||
sector_size image_type |
|||
|
|||
[ -z "${prootfs_size}" ] && \ |
|||
{ echo "root filesystem size not set. Aborting."; exit 1; } |
|||
|
|||
local menderimage_size= |
|||
calculate_sdimg_size $pboot_start $pboot_size \ |
|||
$prootfs_size $data_size \ |
|||
$sector_size pdata_size menderimage_size |
|||
|
|||
echo -e "Creating Mender *.sdimg file:\ |
|||
\nimage size: ${menderimage_size} bytes\ |
|||
\nroot filesystem size: ${prootfs_size} sectors\ |
|||
\ndata partition size: $pdata_size sectors\n" |
|||
|
|||
create_sdimg $menderimage $menderimage_size |
|||
format_sdimg $menderimage $menderimage_size $pboot_start $pboot_size \ |
|||
$prootfs_size $pdata_size $sector_size |
|||
verify_sdimg $menderimage partitions_number |
|||
|
|||
create_device_maps $menderimage sdimgmappings |
|||
make_sdimg_filesystem ${sdimgmappings[@]} |
|||
|
|||
case "$device_type" in |
|||
"beaglebone") |
|||
do_make_sdimg_beaglebone |
|||
;; |
|||
"raspberrypi3") |
|||
do_make_sdimg_raspberrypi3 |
|||
;; |
|||
esac |
|||
|
|||
rc=$? |
|||
|
|||
echo -e "\nCleaning..." |
|||
# Clean and detach. |
|||
detach_device_maps ${sdimgmappings[@]} |
|||
detach_device_maps ${embedmappings[@]} |
|||
sync |
|||
rm -rf $embedded_base_dir |
|||
rm -rf $sdimg_base_dir |
|||
|
|||
[ $rc -eq 0 ] && { echo -e "\n$menderimage image created."; } \ |
|||
|| { echo -e "\n$menderimage image composing failure."; } |
|||
} |
|||
|
|||
do_make_sdimg_beaglebone() { |
|||
local ret=0 |
|||
|
|||
create_device_maps $embeddedimage embedmappings |
|||
|
|||
mount_sdimg ${sdimgmappings[@]} |
|||
mount_embedded ${embedmappings[@]} |
|||
|
|||
echo -e "\nSetting boot partition..." |
|||
stage_2_args="$sdimg_boot_dir $embedded_rootfs_dir" |
|||
${tool_dir}/bbb-convert-stage-2.sh ${stage_2_args} || ret=$? |
|||
[[ $ret -ne 0 ]] && { echo "Aborting."; return $ret; } |
|||
|
|||
echo -e "\nSetting root filesystem..." |
|||
stage_3_args="$sdimg_primary_dir $embedded_rootfs_dir" |
|||
${tool_dir}/bbb-convert-stage-3.sh ${stage_3_args} || ret=$? |
|||
[[ $ret -ne 0 ]] && { echo "Aborting."; return $ret; } |
|||
|
|||
set_fstab $device_type |
|||
|
|||
return $ret |
|||
} |
|||
|
|||
do_make_sdimg_raspberrypi3() { |
|||
image_boot_part=$(fdisk -l ${embeddedimage} | grep FAT32) |
|||
|
|||
boot_part_start=$(echo ${image_boot_part} | awk '{print $2}') |
|||
boot_part_end=$(echo ${image_boot_part} | awk '{print $3}') |
|||
boot_part_size=$(echo ${image_boot_part} | awk '{print $4}') |
|||
|
|||
extract_file_from_image ${embeddedimage} ${boot_part_start} \ |
|||
${boot_part_size} "boot.vfat" |
|||
|
|||
image_rootfs_part=$(fdisk -l ${embeddedimage} | grep Linux) |
|||
|
|||
rootfs_part_start=$(echo ${image_rootfs_part} | awk '{print $2}') |
|||
rootfs_part_end=$(echo ${image_rootfs_part} | awk '{print $3}') |
|||
rootfs_part_size=$(echo ${image_rootfs_part} | awk '{print $4}') |
|||
|
|||
extract_file_from_image ${embeddedimage} ${rootfs_part_start} \ |
|||
${rootfs_part_size} "rootfs.img" |
|||
|
|||
echo -e "\nSetting boot partition..." |
|||
stage_2_args="$output_dir ${sdimgmappings[0]}" |
|||
${tool_dir}/rpi3-convert-stage-2.sh ${stage_2_args} || ret=$? |
|||
[[ $ret -ne 0 ]] && { echo "Aborting."; return $ret; } |
|||
|
|||
echo -e "\nSetting root filesystem..." |
|||
stage_3_args="$output_dir ${sdimgmappings[1]}" |
|||
${tool_dir}/rpi3-convert-stage-3.sh ${stage_3_args} || ret=$? |
|||
[[ $ret -ne 0 ]] && { echo "Aborting."; return $ret; } |
|||
|
|||
mount_sdimg ${sdimgmappings[@]} |
|||
|
|||
# Add mountpoints. |
|||
sudo install -d -m 755 ${sdimg_primary_dir}/uboot |
|||
sudo install -d -m 755 ${sdimg_primary_dir}/data |
|||
|
|||
set_fstab $device_type |
|||
} |
|||
|
|||
do_install_mender() { |
|||
# Mender executables, service and configuration files installer. |
|||
if [ -z "$menderimage" ] || [ -z "$device_type" ] || [ -z "$mender" ] || \ |
|||
[ -z "$artifact" ]; then |
|||
show_help |
|||
exit 1 |
|||
fi |
|||
# mender-image-1.5.0 |
|||
stage_4_args="-i $menderimage -d $device_type -m ${mender} -a ${artifact}" |
|||
|
|||
if [ -n "$demo_ip" ]; then |
|||
stage_4_args="${stage_4_args} -p ${demo_ip}" |
|||
fi |
|||
|
|||
if [ -n "$certificate" ]; then |
|||
stage_4_args="${stage_4_args} -c ${certificate}" |
|||
fi |
|||
|
|||
if [ -n "$production_url" ]; then |
|||
stage_4_args="${stage_4_args} -u ${production_url}" |
|||
fi |
|||
|
|||
if [ -n "${hosted_token}" ]; then |
|||
stage_4_args="${stage_4_args} -o ${hosted_token}" |
|||
fi |
|||
|
|||
eval set -- " ${stage_4_args}" |
|||
|
|||
export -f create_device_maps |
|||
export -f detach_device_maps |
|||
|
|||
${tool_dir}/convert-stage-4.sh ${stage_4_args} |
|||
} |
|||
|
|||
do_install_bootloader() { |
|||
if [ -z "$menderimage" ] || [ -z "$device_type" ] || \ |
|||
[ -z "$toolchain" ]; then |
|||
show_help |
|||
exit 1 |
|||
fi |
|||
|
|||
case "$device_type" in |
|||
"beaglebone") |
|||
stage_5_args="-i $menderimage -d $device_type -t ${toolchain} $keep" |
|||
eval set -- " ${stage_5_args}" |
|||
export -f create_device_maps |
|||
export -f detach_device_maps |
|||
${tool_dir}/bbb-convert-stage-5.sh ${stage_5_args} |
|||
;; |
|||
"raspberrypi3") |
|||
stage_5_args="-i $menderimage -d $device_type -t ${toolchain} $keep" |
|||
eval set -- " ${stage_5_args}" |
|||
export -f create_device_maps |
|||
export -f detach_device_maps |
|||
export -f mount_sdimg |
|||
${tool_dir}/rpi3-convert-stage-5.sh ${stage_5_args} |
|||
;; |
|||
esac |
|||
} |
|||
|
|||
do_make_artifact() { |
|||
if [ -z "${menderimage}" ]; then |
|||
echo "Raw disk .sdimg image not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "${device_type}" ]; then |
|||
echo "Target device_type name not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "${artifact}" ]; then |
|||
echo "Artifact name not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "${rootfs_type}" ]; then |
|||
echo "Artifact name not set. rootfs_a will be used by default." |
|||
rootfs_type="rootfs_a" |
|||
fi |
|||
|
|||
inarray=$(echo ${rootfs_types[@]} | grep -o $rootfs_type | wc -w) |
|||
|
|||
[[ $inarray -eq 0 ]] && \ |
|||
{ echo "Error: invalid rootfs type provided. Aborting."; exit 1; } |
|||
|
|||
local count= |
|||
local bootstart= |
|||
local rootfs_a_start= |
|||
local rootfs_a_size= |
|||
local rootfs_b_start= |
|||
local rootfs_b_size= |
|||
local rootfs_path= |
|||
local sdimg_device_type= |
|||
local abort=0 |
|||
|
|||
get_sdimg_info $menderimage count sector_size rootfs_a_start rootfs_a_size \ |
|||
rootfs_b_start rootfs_b_size |
|||
ret=$? |
|||
[[ $ret -ne 0 ]] && \ |
|||
{ echo "Error: cannot validate input .sdimg image. Aborting."; exit 1; } |
|||
|
|||
create_device_maps $menderimage sdimgmappings |
|||
mount_sdimg ${sdimgmappings[@]} |
|||
|
|||
if [[ $rootfs_type == "rootfs_a" ]]; then |
|||
prootfs_size=$rootfs_a_size |
|||
rootfs_path=$sdimg_primary_dir |
|||
elif [[ $rootfs_type == "rootfs_b" ]]; then |
|||
prootfs_size=$rootfs_b_size |
|||
rootfs_path=$sdimg_secondary_dir |
|||
fi |
|||
|
|||
# Find .sdimg file's dedicated device type. |
|||
sdimg_device_type=$( cat $sdimg_data_dir/mender/device_type | sed 's/[^=].*=//' ) |
|||
|
|||
# Set 'artifact name' as passed in the command line. |
|||
sudo sed -i '/^artifact/s/=.*$/='${artifact}'/' "$rootfs_path/etc/mender/artifact_info" |
|||
|
|||
if [ "$sdimg_device_type" != "$device_type" ]; then |
|||
echo "Error: .mender and .sdimg device type not matching. Aborting." |
|||
abort=1 |
|||
fi |
|||
|
|||
if [[ $(which mender-artifact) = 1 ]]; then |
|||
echo "Error: mender-artifact not found in PATH. Aborting." |
|||
abort=1 |
|||
fi |
|||
|
|||
if [ $abort -eq 0 ]; then |
|||
local rootfs_file=${output_dir}/rootfs.ext4 |
|||
|
|||
echo "Creating a ext4 file-system image from modified root file-system" |
|||
dd if=/dev/zero of=$rootfs_file seek=${prootfs_size} count=0 bs=512 status=none |
|||
|
|||
sudo mkfs.ext4 -FF $rootfs_file -d $rootfs_path |
|||
|
|||
fsck.ext4 -fp $rootfs_file |
|||
|
|||
mender_artifact=${output_dir}/${device_type}_${artifact}.mender |
|||
echo "Writing Mender artifact to: ${mender_artifact}" |
|||
|
|||
#Create Mender artifact |
|||
mender-artifact write rootfs-image \ |
|||
--update ${rootfs_file} \ |
|||
--output-path ${mender_artifact} \ |
|||
--artifact-name ${artifact} \ |
|||
--device-type ${device_type} |
|||
|
|||
ret=$? |
|||
[[ $ret -eq 0 ]] && \ |
|||
{ echo "Writing Mender artifact to ${mender_artifact} succeeded."; } || \ |
|||
{ echo "Writing Mender artifact to ${mender_artifact} failed."; } |
|||
|
|||
rm $rootfs_file |
|||
fi |
|||
|
|||
# Clean and detach. |
|||
detach_device_maps ${sdimgmappings[@]} |
|||
|
|||
rm -rf $sdimg_base_dir |
|||
} |
|||
|
|||
do_make_all() { |
|||
do_make_sdimg |
|||
do_install_mender |
|||
do_install_bootloader |
|||
} |
|||
|
|||
#read -s -p "Enter password for sudo: " sudoPW |
|||
#echo "" |
|||
|
|||
PARAMS="" |
|||
|
|||
# Load necessary functions. |
|||
source ${tool_dir}/mender-conversion-functions.sh |
|||
|
|||
while (( "$#" )); do |
|||
case "$1" in |
|||
-r | --rootfs-type) |
|||
rootfs_type=$2 |
|||
shift 2 |
|||
;; |
|||
-i | --image) |
|||
menderimage=$2 |
|||
shift 2 |
|||
;; |
|||
-e | --embedded) |
|||
embeddedimage=$(get_path $2) |
|||
shift 2 |
|||
;; |
|||
-s | --size-data) |
|||
data_size=$2 |
|||
shift 2 |
|||
;; |
|||
-d | --device-type) |
|||
device_type=$2 |
|||
shift 2 |
|||
;; |
|||
-a | --artifact) |
|||
artifact=$2 |
|||
shift 2 |
|||
;; |
|||
-m | --mender) |
|||
mender=$(get_path $2) |
|||
shift 2 |
|||
;; |
|||
-t | --toolchain) |
|||
toolchain=$2 |
|||
shift 2 |
|||
;; |
|||
-p | --demo-ip) |
|||
demo_ip=$2 |
|||
shift 2 |
|||
;; |
|||
-c | --certificate) |
|||
certificate=$2 |
|||
shift 2 |
|||
;; |
|||
-u | --production-url) |
|||
production_url=$2 |
|||
shift 2 |
|||
;; |
|||
-o | --hosted-token) |
|||
hosted_token=$2 |
|||
shift 2 |
|||
;; |
|||
-k | --keep) |
|||
keep="-k" |
|||
shift 1 |
|||
;; |
|||
-h | --help) |
|||
show_help |
|||
exit 0 |
|||
;; |
|||
--) |
|||
shift |
|||
break |
|||
;; |
|||
-*) |
|||
echo "Error: unsupported option $1" >&2 |
|||
exit 1 |
|||
;; |
|||
*) |
|||
PARAMS="$PARAMS $1" |
|||
shift |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
[ -z "${data_size}" ] && \ |
|||
{ echo "Default 'data' partition size set to 128MiB"; data_size=128; } |
|||
|
|||
eval set -- "$PARAMS" |
|||
|
|||
# Some commands expect elevated privileges. |
|||
sudo true |
|||
|
|||
case "$1" in |
|||
prepare_image) |
|||
do_prepare_image |
|||
;; |
|||
make_sdimg) |
|||
do_make_sdimg |
|||
;; |
|||
install_mender) |
|||
do_install_mender |
|||
;; |
|||
install_bootloader) |
|||
do_install_bootloader |
|||
;; |
|||
make_artifact) |
|||
do_make_artifact |
|||
;; |
|||
make_all) |
|||
do_make_all |
|||
;; |
|||
*) |
|||
show_help |
|||
;; |
|||
esac |
|||
|
@ -0,0 +1,24 @@ |
|||
#!/bin/bash |
|||
|
|||
output_dir=$1 |
|||
boot_mapping=$2 |
|||
|
|||
[ ! -f $output_dir/boot.vfat ] && \ |
|||
{ echo "Error: extracted boot partition not found. Aborting."; exit 1; } |
|||
|
|||
# Make a copy of Linux kernel arguments and modify. |
|||
mcopy -o -i ${output_dir}/boot.vfat -s ::cmdline.txt ${output_dir}/cmdline.txt |
|||
sed -i 's/\b[ ]root=[^ ]*/ root=\/dev\/mmcblk0p2/' ${output_dir}/cmdline.txt |
|||
sed -i 's/\b[ ]console=tty1//' ${output_dir}/cmdline.txt |
|||
# Update Linux kernel command arguments with our custom configuration |
|||
mcopy -o -i ${output_dir}/boot.vfat -s ${output_dir}/cmdline.txt ::cmdline.txt |
|||
|
|||
mcopy -i ${output_dir}/boot.vfat -s ::config.txt ${output_dir}/config.txt |
|||
echo -e '\nenable_uart=1\n' >> ${output_dir}/config.txt |
|||
mcopy -o -i ${output_dir}/boot.vfat -s ${output_dir}/config.txt ::config.txt |
|||
|
|||
sudo dd if=${output_dir}/boot.vfat of=/dev/mapper/${boot_mapping} bs=1M |
|||
|
|||
echo -e "\nStage done." |
|||
|
|||
exit 0 |
@ -0,0 +1,16 @@ |
|||
#!/bin/bash |
|||
|
|||
output_dir=$1 |
|||
rootfs_mapping=$2 |
|||
|
|||
[ ! -f ${output_dir}/rootfs.img ] && \ |
|||
{ echo "Error: extracted rootfs partition not found. Aborting."; exit 1; } |
|||
|
|||
sudo dd if=${output_dir}/rootfs.img of=/dev/mapper/${rootfs_mapping} bs=8M |
|||
|
|||
# dd sets the original label, make sure label follows Mender naming convention. |
|||
sudo e2label /dev/mapper/${rootfs_mapping} "primary" |
|||
|
|||
echo -e "\nStage done." |
|||
|
|||
exit 0 |
@ -0,0 +1,179 @@ |
|||
#!/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 )" |
|||
output_dir=${application_dir}/output |
|||
bin_dir_pi=${output_dir}/bin/raspberrypi |
|||
sdimg_base_dir=$output_dir/sdimg |
|||
|
|||
echo "Running: $(basename $0)" |
|||
declare -a sdimgmappings |
|||
declare -a sdimg_partitions=("boot" "primary" "secondary" "data") |
|||
|
|||
build_uboot_files() { |
|||
mkdir -p $bin_dir_pi |
|||
|
|||
echo -e "Downloading U-Boot related files..." |
|||
|
|||
wget -q -O $bin_dir_pi/boot.scr \ |
|||
https://github.com/mirzak/mender-conversion-tools/raw/mirza/wip/bin/raspberrypi/boot.scr |
|||
wget -q -O $bin_dir_pi/fw_printenv \ |
|||
https://github.com/mirzak/mender-conversion-tools/raw/mirza/wip/bin/raspberrypi/fw_printenv |
|||
wget -q -O $bin_dir_pi/init_resize.sh \ |
|||
https://raw.githubusercontent.com/mirzak/mender-conversion-tools/mirza/wip/bin/raspberrypi/init_resize.sh |
|||
wget -q -O $bin_dir_pi/u-boot.bin \ |
|||
https://github.com/mirzak/mender-conversion-tools/raw/mirza/wip/bin/raspberrypi/u-boot.bin |
|||
} |
|||
|
|||
# Takes following arguments: |
|||
# |
|||
# $1 - boot partition mountpoint |
|||
# $2 - primary partition mountpoint |
|||
install_files() { |
|||
local boot_dir=$1 |
|||
local rootfs_dir=$2 |
|||
|
|||
# Make a copy of Linux kernel arguments and modify. |
|||
cp ${boot_dir}/cmdline.txt ${output_dir}/cmdline.txt |
|||
|
|||
sed -i 's/\b[ ]root=[^ ]*/ root=\${mender_kernel_root}/' ${output_dir}/cmdline.txt |
|||
|
|||
# If the the image that we are trying to convert has been booted once on a |
|||
# device, it will have removed the init_resize.sh init argument from cmdline. |
|||
# |
|||
# But we want it to run on our image as well to resize our data part so in |
|||
# case it is missing, add it back to cmdline.txt |
|||
if ! grep "init=/usr/lib/raspi-config/init_resize.sh" ${output_dir}/cmdline.txt; then |
|||
cmdline=$(cat ${output_dir}/cmdline.txt) |
|||
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 instea 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}/kernel7.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}/kernel7.img |
|||
sudo cp ${bin_dir_pi}/boot.scr ${boot_dir} |
|||
sudo cp ${bin_dir_pi}/init_resize.sh ${rootfs_dir}/usr/lib/raspi-config/init-resize.sh |
|||
|
|||
sudo cp ${boot_dir}/config.txt ${output_dir}/config.txt |
|||
|
|||
# dtoverlays seems to break U-boot for some reason, simply remove all of |
|||
# them as they do not actually work when U-boot is used. |
|||
sed -i /^dtoverlay=/d ${output_dir}/config.txt |
|||
|
|||
sudo cp ${output_dir}/config.txt ${boot_dir}/config.txt |
|||
|
|||
# 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 part instead of rootfs, which it |
|||
# normally expands in standard Raspberry Pi distributions. |
|||
sudo install -m 755 ${bin_dir_pi}/init_resize.sh \ |
|||
${rootfs_dir}/usr/lib/raspi-config/init_resize.sh |
|||
} |
|||
|
|||
do_install_bootloader() { |
|||
echo "Setting bootloader..." |
|||
|
|||
if [ -z "${image}" ]; then |
|||
echo ".sdimg image file not set. Aborting." |
|||
exit 1 |
|||
fi |
|||
|
|||
[ ! -f $image ] && { echo "$image - file not found. Aborting."; exit 1; } |
|||
|
|||
# Map & mount Mender compliant image. |
|||
create_device_maps $image sdimgmappings |
|||
|
|||
mkdir -p $output_dir && cd $output_dir |
|||
|
|||
# Build patched U-Boot files. |
|||
build_uboot_files |
|||
|
|||
mount_sdimg ${sdimgmappings[@]} |
|||
|
|||
install_files ${output_dir}/sdimg/boot ${output_dir}/sdimg/primary |
|||
|
|||
detach_device_maps ${sdimgmappings[@]} |
|||
|
|||
echo -e "\nStage done." |
|||
} |
|||
|
|||
# Conditional once we support other boards |
|||
PARAMS="" |
|||
|
|||
while (( "$#" )); do |
|||
case "$1" in |
|||
-i | --image) |
|||
image=$2 |
|||
shift 2 |
|||
;; |
|||
-t | --toolchain) |
|||
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 |
|||
;; |
|||
-*) |
|||
echo "Error: unsupported option $1" >&2 |
|||
exit 1 |
|||
;; |
|||
*) |
|||
PARAMS="$PARAMS $1" |
|||
shift |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
eval set -- "$PARAMS" |
|||
|
|||
# Some commands expect elevated privileges. |
|||
sudo true |
|||
|
|||
do_install_bootloader |
Loading…
Reference in new issue