LmP Customization

This page covers common ways to modify the Linux® microPlatform (LmP). This includes kernel options, startup services, user and group configuration, and other options related to the platform build.

See also

The tutorial for Customizing the Platform covers adding a custom recipe service. This is a good place to start, as it introduces concepts and steps related to customizing LmP.

Customizing the Distro

This section shows how to build new Targets with a different distro. The following is an example on how to build a new Target with LmP XWayland.

  1. Update your factory config:

    git clone https://source.foundries.io/factories/<factory>/ci-scripts.git
    cd ci-scripts
    vi factory-config.yml
    
  2. Add the distro under LmP:

    worker_tag: "amd64-partner-aws"
    params:
       IMAGE: lmp-factory-image
       DOCKER_COMPOSE_APP: "1"
       DISTRO: "lmp-xwayland"
    
  3. Commit and push the changes:

    git add .
    git commit -m "Adding lmp-xwayland as base for my factory"
    git push
    
  4. Changes to the ci-script repository do not auto-trigger builds. To do so, push a commit in lmp-manifest or meta-subscriber-overrides:

    git clone https://source.foundries.io/factories/<factory>/lmp-manifest.git
    cd lmp-manifest
    git commit -m "trigger build" --allow-empty
    

Kernel Command Line Arguments

The method of modifying kernel command line arguments differs between what LmP distro you have set. The two options available are lmp (default) and lmp-base.

See the LmP Distros page for an overview of the two options.

Distro: lmp

Extend the kernel command line by setting OSTREE_KERNEL_ARGS in meta-subscriber-overrides/conf/machine/include/lmp-factory-custom.inc:

OSTREE_KERNEL_ARGS:<machine> = "console=${console} <new-args> ${OSTREE_KERNEL_ARGS_COMMON}"

Make sure you set the correct <machine> and other variables as needed.

Note

The default is OSTREE_KERNEL_ARGS_COMMON ?= "root=LABEL=otaroot rootfstype=ext4". This variable sets a valid root label for the device. It is not necessary on devices specifying the partition path directly with root=.

Distro: lmp-base

Extend the kernel command line by appending your commands to bootcmd_args in meta-subscriber-overrides/recipes-bsp/u-boot/u-boot-base-scr/<machine>/uEnv.txt.in. For example:

bootcmd_args=setenv bootargs console=tty1 console=${console} root=/dev/mmcblk2p2 rootfstype=ext4 rootwait rw <new-args>

Create meta-subscriber-overrides/recipes-bsp/u-boot/u-boot-base-scr.bbappend with the following content to include your source file:

FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

Note

If testing a board supported in meta-lmp, the default uEnv.txt.in can be found in meta-lmp/meta-lmp-bsp/recipes-bsp/u-boot/u-boot-base-scr/<machine>/uEnv.txt.in.

Automatically Loading a Kernel Module

Configure to only load a natively supported kernel module, such as i2c-dev, by adding the following change in conf/machine/include/lmp-factory-custom.inc:

KERNEL_MODULE_AUTOLOAD:<machine> = "i2c-dev"

Note

To autoload an out of tree kernel module, please refer to Adding a New Kernel Driver.

Adding a new Systemd Startup Service

LmP uses systemd for service management. The tutorial on Customizing the Platform provides a detailed walk-through of the steps required for adding a systemd service. A summarized example for adding a shell script to run at startup is provided below for quick reference. You should first be familiar with editing the meta-subscribers-overrides layer.

Important

Make sure to replace <service-name> accordingly throughout the instructions below.

  1. Create a directory for your service in meta-subscriber-overrides repo:

    mkdir -p recipes-support/<service-name>
    
  2. Add a new file named <service-name>.bb under this directory, with the following content:

    SUMMARY = "Description of your service"
    LICENSE = "MIT"
    LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
    
    inherit allarch systemd
    
    SRC_URI = " \
            file://<service-name>.service \
            file://<service-name>.sh \
    "
    
    S = "${WORKDIR}"
    
    PACKAGE_ARCH = "${MACHINE_ARCH}"
    
    SYSTEMD_SERVICE:${PN} = "<service-name>.service"
    SYSTEMD_AUTO_ENABLE:${PN} = "enable"
    
    do_install () {
            install -d ${D}${bindir}
            install -m 0755 ${WORKDIR}/<service-name>.sh ${D}${bindir}/<service-name>.sh
    
            install -d ${D}${systemd_system_unitdir}
            install -m 0644 ${WORKDIR}/<service-name>.service ${D}${systemd_system_unitdir}
    }
    
    FILES:${PN} += "${systemd_system_unitdir}/<service-name>.service"
    FILES:${PN} += "${systemd_unitdir}/system-preset"
    
  3. Create another directory with the same name as the one we just created to place the source file(s) for the recipe:

    recipes-support/<service-name>/<service-name>
    
  4. Create the systemd service file <service-name>.service under this new directory, configuring it to meet your needs:

    [Unit]
    Description=A description of your service
    After=rc-local.service
    
    [Service]
    Type=oneshot
    LimitNOFILE=1024
    ExecStart=/usr/bin/<service-name>.sh
    RemainAfterExit=true
    Environment=HOME=/home/root
    
  5. Add the <service-name>.sh script to run at startup under this new directory:

    #!/bin/sh
    #
    # SPDX-License-Identifier: Apache 2.0
    #
    # Copyright (c) 2021, Foundries.io Ltd.
    
    # NOTE: This script will always exit with 0 result as other services
    # are dependent on it.
    
    # break on errors
    set -e
    
    echo "Hello World"
    exit 0
    

    Note

    If testing script locally, remember to make it executable.

  6. Remember to install the new service by appending the CORE_IMAGE_BASE_INSTALL variable in lmp-factory-image.bb:

    CORE_IMAGE_BASE_INSTALL += " \
    <service-name> \
    "
    
  7. Lastly, check that the service is starting. From the device:

    systemctl status <service-name>.service

Setting a Static IP on the Device

While this example shows how to configure the eth1 interface, the steps can be extended for other net interfaces.

  1. First, create the .bbappend file, recipes-connectivity/networkmanager/networkmanager_%.bbappend:

    FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
    
    SRC_URI:append = " \
        file://eth1.nmconnection \
    "
    
    do_install:append () {
        install -d ${D}${sysconfdir}/NetworkManager/system-connections
        install -m 0600 ${WORKDIR}/eth1.nmconnection ${D}${sysconfdir}/NetworkManager/system-connections
    
  2. Now add the configuration fragment in recipes-connectivity/networkmanager/networkmanager/eth1.nmconnection:

    [connection]
    id=Wired connection 1
    uuid=7a0a09e1-6a0e-449f-9d51-9f48ba411edf
    type=ethernet
    autoconnect-priority=-999
    interface-name=eth1
    
    [ipv4]
    address1=<static-ip>/24,<gateway-address>
    method=manual
    
    [ipv6]
    addr-gen-mode=stable-privacy
    method=auto
    

Important

Remember to adjust the address1 parameter as needed.

LmP Users and Groups

Users and groups can be added and configured prior to building an image.

Extending User Groups

Tip

The default LmP group and password tables can be found in meta-lmp/meta-lmp-base/files.

To define a new user group in a Factory:

  1. Define a custom group table in meta-subscriber-overrides/files/custom-group-table with the wanted user groups with <username>:x:<user-id>. For example:

    systemd-coredump:x:998:
    
  2. Define a custom passwd table in meta-subscriber-overrides/files/custom-passwd-table for the new user group: <username>:x:<user-id>:<group-id>::<home-dir>:<command>. For example:

    systemd-coredump:x:998:998::/:/sbin/nologin
    

    Note

    This example works for system groups and system users (user-id less than 1000). For normal users, check Adding LmP Users.

    Important

    Platform build errors like below are fixed after extending the user group: normal groupname `<group>` does not have a static ID defined.

  3. Add these files to the build in meta-subscriber-overrides/conf/machine/include/lmp-factory-custom.inc:

    USERADD_GID_TABLES += "files/custom-group-table"
    USERADD_UID_TABLES += "files/custom-passwd-table"
    

Adding LmP Users

  1. To create a new LmP user or replace the default fio user, first add the new user to the system. The steps are similar to the ones described in Extending User Groups. However normal users need a valid shell and user-id higher than 1000 for adding a new user, or equal to 1000 if replacing the fio user. For example:

    group-table:

    test-user:x:1001:
    

    passwd-table:

    test-user:x:1001:1001::/home/test-user:/bin/sh
    
  2. To create the password for this new user, run from a host computer mkpasswd -m sha512crypt. When prompted for password, enter the desired password for the user. This returns the hashed password. For example:

    mkpasswd -m sha512crypt
    Password:
    $6$OJHEGl4Dk5nEwG6k$z19R1jc7cCfcQigX78cUH1Qzf2HINfB6dn6WgKmMLWgg967AV3s3tuuJE7uhLmBK.bHDpl8H5Ab/B3kNvGE1E.
    
  3. Edit the result from the previous command to escape any $ characters, for example:

    \$6\$OJHEGl4Dk5nEwG6k\$z19R1jc7cCfcQigX78cUH1Qzf2HINfB6dn6WgKmMLWgg967AV3s3tuuJE7uhLmBK.bHDpl8H5Ab/B3kNvGE1E.
    
  4. You can also escape any special characters by using the printf command in bash:

    password_hash=`mkpasswd -m sha512crypt`
    printf '%q' "$password_hash"
    

    This is the USER_PASSWD/LMP_PASSWORD to be added to the build as the new user password.

  5. If including a new user, add the following block to meta-subscriber-overrides/recipes-samples/images/lmp-factory-image.bb:

    USER_PASSWD = "\$6\$OJHEGl4Dk5nEwG6k\$z19R1jc7cCfcQigX78cUH1Qzf2HINfB6dn6WgKmMLWgg967AV3s3tuuJE7uhLmBK.bHDpl8H5Ab/B3kNvGE1E."
    
    EXTRA_USERS_PARAMS += "\
    groupadd <user>; \
    useradd -p '${USER_PASSWD}' <user>; \
    usermod -a -G sudo,users,plugdev <user>; \
    "
    

    Or if replacing the fio user, add the following to meta-subscriber-overrides/conf/machine/include/lmp-factory-custom.inc:

    LMP_USER = "<user>"
    LMP_PASSWORD = "\$6\$OJHEGl4Dk5nEwG6k\$z19R1jc7cCfcQigX78cUH1Qzf2HINfB6dn6WgKmMLWgg967AV3s3tuuJE7uhLmBK.bHDpl8H5Ab/B3kNvGE1E."
    

    Note

    Remember to replace USER_PASSWD, <user> and LMP_PASSWORD accordingly.

After these changes, the files /usr/lib/passwd and /usr/lib/group should include the configuration for the new user.

LmP Time Servers

By default, LmP does time synchronization using systemd-timesyncd. It is recommended to use that whenever possible as it is well integrated with systemd. However, a common request is to enable Network Time Protocol (NTP) as a time server instead.

For that, first disable systemd-timesyncd support in meta-subscriber-overrides/recipes-core/systemd/systemd_%.bbappend:

PACKAGECONFIG:remove = "timesyncd"

Then, enable ntp in meta-subscriber-overrides/recipes-samples/images/lmp-factory-image.bb by appending the CORE_IMAGE_BASE_INSTALL variable:

CORE_IMAGE_BASE_INSTALL += " \
    ntp \
"

Note

If systemd-timesyncd is used, the default ntp server list is set in this recipe:

DEF_FALLBACK_NTP_SERVERS ?= "time1.google.com time2.google.com time3.google.com time4.google.com time.cloudflare.com"

If needed, this can be customized in meta-subscriber-overrides/recipes-core/systemd/systemd_%.bbappend:

DEF_FALLBACK_NTP_SERVERS += " <new-server>"

Installing Files Under var

Anything created under /var gets removed when creating the OSTree deployment. For this reason, a recipe can only install content under it using tmpfiles.

An example of using tmpfiles to create a directory under /var can be found in meta-lmp collectd.bbappend, where tmpfiles.conf shows the directory to be created.

Tip

Files can also be created dynamically using a runtime service. See how to add a Custom Systemd Service.