# Integrating Mender State Scripts in Yocto - A Step by Step Guide

**URL:** <https://hub.mender.io/t/integrating-mender-state-scripts-in-yocto-a-step-by-step-guide/4997>\
**Category:** Tutorials\
**Created:** [June 3, 2022, 8:43am UTC](https://hub.mender.io/t/integrating-mender-state-scripts-in-yocto-a-step-by-step-guide/4997 "2022-06-03T08:43:06Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![karolina.beta](https://avatars.discourse-cdn.com/v4/letter/k/8baadc/32.png) [@karolina.beta](https://hub.mender.io/u/karolina.beta)\
**Post date:** [June 3, 2022, 8:43am UTC](https://hub.mender.io/t/integrating-mender-state-scripts-in-yocto-a-step-by-step-guide/4997/1 "2022-06-03T08:43:06Z")

</div>

> **[Integrating Mender State Scripts in Yocto - A Step by Step Guide | Mender](https://mender.io/blog/integrating-mender-state-scripts-in-yocto)**
>
> This tutorial will explain specifically the concept of state scripts in the OTA solution Mender.io and give an example of how to integrate them into Yocto

# Integrating Mender State Scripts in Yocto - A Step by Step Guide

 ![mender-state-machine](https://canada1.discourse-cdn.com/flex036/uploads/mender/original/2X/3/3c97caee4b0e4570744216795a89a612e12c2b70.png)

This tutorial will explain the concept of state scripts in mender and give an example on how to integrate them into yocto.

## Prerequisites

- [kas](https://kas.readthedocs.io/en/latest/userguide.html#dependencies-installation) installed on the system
- Raspberry pi 4 with an SDCard available
- No explanation is needed on how to flash and SDcard

This tutorial has been tested with Ubuntu 20.04.

## Setting the field

The example will be shown on the Raspberry Pi 4.  
To start the example a working Yocto build with mender integration must be set up.

### Configuring the build

Please save the [linked content](#mender-rpi4yml-16) into a file called `mender-rpi4.yml`.

Set the `MENDER_TENANT_TOKEN` the [value associated with your account](#how-to-find-the-value-of-the-tenant-token-13).

### Building the basic image

Once that is set run the command below and expect a few hours for the build process to finish.

```auto
kas shell mender-rpi4.yml
bitbake core-image-minimal

```

[Gather the build files](#gathering-the-build-output-14) and [upload them to Hosted Mender or flash to the SDCard](#uploading-and-flashing-15).

## Introducing state scripts

[State scripts](https://docs.mender.io/overview/customize-the-update-process) are a way to customize the update process. This is a similar concept to the pre-install or post-install scripts mechanism provided by the [debian package management system](https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html#package-maintainer-scripts-and-installation-procedure).

Without the scripts, the unmodified update process goes through a list of [states](https://docs.mender.io/overview/customize-the-update-process#states) from start to finish. In a simplified manner it looks like this:

 ![Unmodified update process (1)](https://canada1.discourse-cdn.com/flex036/uploads/mender/original/2X/9/9aaed125fafbfefcdc2ad90ae22c3921ed802de9.png)

State scripts allow extension of those states with custom steps:

 ![State script in a non error state (1)](https://canada1.discourse-cdn.com/flex036/uploads/mender/original/2X/e/e2b332ec5a9966390fbd2e903121659f9bf715b1.png)

The image shows all the state scripts for a non error case, you can implement multiple for the same state or none at all.

### How do the state scripts reach the device?

There are two ways for state scripts to reach the device and get executed:

- it comes embedded in the update which is taking place - Artifact state scripts
- it exists on the device up front before the update is started - Root file system state scripts

 ![Ways for state scripts to reach the device (1)](https://canada1.discourse-cdn.com/flex036/uploads/mender/original/2X/e/e7320ee9f8fa7bfa27013be789e5a637f62e6844.png)

### How to include Mender State Scripts in the Yocto Build Process

Including the state scripts into yocto will require a creation of a dedicated recipe which uses a helper class for installing state scripts.

The steps below, will create the new layer with the state scripts recipe in it.

```auto
kas shell mender-rpi4.yml
bitbake-layers create-layer meta-example
bitbake-layers add-layer meta-example

mkdir -p meta-example/recipes-example/state-scripts/files

cat > meta-example/recipes-example/state-scripts/files/Download_Leave_10 << EOF
#!/usr/bin/env sh
>&2 echo "Printed from Download_Leave_10"
EOF

cat > meta-example/recipes-example/state-scripts/files/ArtifactInstall_Enter_10 << EOF
#!/usr/bin/env sh
>&2 echo "Printed from ArtifactInstall_Enter_10"
EOF

cat > meta-example/recipes-example/state-scripts/state-scripts.bb << "EOF"
LICENSE="CLOSED"
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
SRC_URI += "file://Download_Leave_10;subdir=${BPN}-${PV} \
            file://ArtifactInstall_Enter_10;subdir=${BPN}-${PV}"
inherit mender-state-scripts

do_compile() {
    cp Download_Leave_10 ${MENDER_STATE_SCRIPTS_DIR}/Download_Leave_10
    cp ArtifactInstall_Enter_10 ${MENDER_STATE_SCRIPTS_DIR}/ArtifactInstall_Enter_10
}

EOF

```

With the recipe and layer created, the state-script recipe must now be included into the build.  
In addition to that a new name should be given to separate the newly built files from the old ones.

```auto
cat >> conf/local.conf << EOF
IMAGE_INSTALL_append = " state-scripts"
EOF

sed -i 's/^MENDER_ARTIFACT_NAME.*/MENDER_ARTIFACT_NAME = "rpi4-kas-build-state-scripts"/g' conf/local.conf

```

### Building the image with state scripts

Once that is done kick off the build.

```auto
bitbake core-image-minimal

```

[Gather the build files](#gathering-the-build-output-14) and [upload them to Hosted Mender](#uploading-and-flashing-15).

### Seeing the scripts in action

As you deploy the new release to your board the Artifact state script (`ArtifactInstall_Enter_10`) will be executed.  
This can be tracked by going to the device shell (either via UART/SSH or Remote Terminal) and running `journalctl -fu mender-client`.

You should see an output like this when doing an update from version `rpi4-kas-build` to `rpi4-kas-build-state-scripts`:

```auto
level=info msg="Executing script: ArtifactInstall_Enter_10"
level=info msg="Collected output (stderr) while running script /var/lib/mender/scripts/ArtifactInstall_Enter_10\nPrinted from ArtifactInstall_Enter_10\n\n---------- end of script output"

```

There is no output for the `Download_Leave_10` as it is a Root file system state scripts and it isn’t present on the `rpi4-kas-build` version. However as the device is now running `rpi4-kas-build-state-scripts`, this version has `/etc/mender/scripts/Download_Leave_10` present.

As a result of this doing an update from version `rpi4-kas-build-state-scripts` to `rpi4-kas-build` will give the following output:

```auto
level=info msg="Executing script: Download_Leave_10"
level=info msg="Collected output (stderr) while running script /etc/mender/scripts/Download_Leave_10\nPrinted from Download_Leave_10\n\n---------- end of script output"

```

## Conclusion

In this tutorial we have shown how to extend the default Mender OTA behaviour with state scripts.  
The concept behind the states scripts was explained where they were conceptually compared to the pre and post install scripts of package managers.  
Additionally an example is given on integrating the state scripts in the Yocto build process which required the creation of a custom recipe.

## Appendix

These are chapters and know-how steps which are referenced throughout the tutorial.

### How to find the value of the tenant token?

To get the tenant token value, log into Hosted Mender, from the upper left corner select `My Organisation` and copy the `Organisation token` to clipboard.

This is the value of your `MENDER_TENANT_TOKEN`.

![getting-the-token](https://canada1.discourse-cdn.com/flex036/uploads/mender/original/2X/3/3d28840ef2a70312f0a2a7887f4b32607470fb56.gif)

[Back to “Configuring the build”](#configuring-the-build-4)

### Gathering the build output

As a result of the build Yocto creates a lot of files buried deep into the directory structure.  
From a practical stand point, we only need two.

The command below will gather needed files in the `artifacts` into an accessible directory.

```auto
# Executed from the build dir (bitbake in path)
mkdir artifacts

BITBAKE_ENV=$(bitbake -e core-image-minimal | grep -E "^DEPLOY_DIR_IMAGE=|^IMAGE_LINK_NAME")
export $(echo $BITBAKE_ENV | sed 's/"//g')
cp ${DEPLOY_DIR_IMAGE}/${IMAGE_LINK_NAME}.mender artifacts
cp ${DEPLOY_DIR_IMAGE}/${IMAGE_LINK_NAME}.sdimg artifacts

```

- [Back to “Building the basic image”](#building-the-basic-image-5)
- [Back to “Building the image with state scripts”](#building-the-image-with-state-scripts-9)

### Uploading and flashing

Two artifacts will be available in the `artifacts` directory.  
They have different purposes:

- `.sdimage`
  - flash it directly on the SDCard
  - this only needs to be done once

- `.mender`
  - represents the release which can be deployed to a device over the air

Please flash your SDCard with the `.sdimg`, place it in the Raspberry Pi 4 and power it up.  
The device should soon be visible in Hosted Mender as pending.

In addition to that please upload the `.mender` file (Mender artifact) to Hosted Mender.  
Log into Hosted Mender, select `Releases` from the menu on the left and click `Upload`.

### mender-rpi4.yml

The content below should be copied to the file called `mender-rpi4.yml`.

```auto
header:
  version: 10

machine: raspberrypi4

repos:
  poky:
    url: https://git.yoctoproject.org/git/poky
    refspec: "dunfell"
    layers:
      meta:
      meta-poky:
      meta-yocto-bsp:

  meta-openembedded:
    url: http://git.openembedded.org/meta-openembedded
    refspec: "dunfell"
    layers:
      meta-oe:
      meta-python:
      meta-networking:
      meta-multimedia:

  meta-raspberrypi:
    url: https://github.com/agherzan/meta-raspberrypi
    refspec: "dunfell"
    layers:
      .:

  meta-mender:
    url: https://github.com/mendersoftware/meta-mender
    refspec: "dunfell"
    layers:
      meta-mender-core:
      meta-mender-demo:
      meta-mender-raspberrypi:

bblayers_conf_header:
  standard: |
    POKY_BBLAYERS_CONF_VERSION = "1"
    BBPATH = "${TOPDIR}"
    BBFILES ?= ""

local_conf_header:
  enable_systemd: |
    DISTRO_FEATURES_append = " systemd"
    VIRTUAL-RUNTIME_init_manager = "systemd"
    VIRTUAL-RUNTIME_initscripts = ""

  general_helpers: |
    INHERIT += "rm_work"
    IMAGE_FEATURES += "ssh-server-openssh allow-empty-password debug-tweaks"
    IMAGE_INSTALL_append = " python3"

  rpi_helpers: |
    ENABLE_UART = "1"
    RPI_EXTRA_CONFIG="dtoverlay=disable-bt"

  mender_device_integration: |
    IMAGE_LINK_NAME_append = "-${MENDER_ARTIFACT_NAME}"
    INHERIT += "mender-full"
    RPI_USE_U_BOOT = "1"
    IMAGE_FSTYPES_remove += " rpi-sdimg"
    MENDER_FEATURES_ENABLE_append = " mender-uboot mender-image-sd"
    MENDER_FEATURES_DISABLE_append = " mender-grub mender-image-uefi"
    MENDER_BOOT_PART_SIZE_MB = "40"

  dynamic: |
    MENDER_ARTIFACT_NAME = "rpi4-kas-build"
    MENDER_SERVER_URL = "https://hosted.mender.io"
    MENDER_TENANT_TOKEN = "" <--- Insert correct value

```

[Back to “Configuring the build”](#configuring-the-build-4)
