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.

Tip

Default LmP group and password tables can be found in meta-lmp/meta-lmp-base/files. This includes lmp-passwd-table, lmp-group-table, and for the default user, lmp-passwd-table-default and lmp-group-default.

Extending User Groups#

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 New 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 New LmP Users#

  1. To create a new LmP user, first add the new user to the system. The steps are similar to the ones described in Extending User Groups. Normal users need a valid shell and user-id higher than 1000. 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, run mkpasswd -m sha512crypt from a host computer. When prompted, enter the desired password for the user. This returns the hashed password:

    $ mkpasswd -m sha512crypt
    Password:
    $6$OJHEGl4Dk5nEwG6k$z19R1jc7cCfcQigX78cUH1Qzf2HINfB6dn6WgKmMLWgg967AV3s3tuuJE7uhLmBK.bHDpl8H5Ab/B3kNvGE1E.
    
  3. Escape special characters by editing manually or by using the printf command from your shell, instead of the above:

    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.

  4. 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>; \
    "
    

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

Replacing Default fio User#

Tip

See lmp-passwd-table-default and lmp-group-table-default for fio user defaults.

To replacing the default user fio, follow the steps for Adding New LmP Users; For the user-id, the value should be 1000.

Add the following to meta-subscriber-overrides/conf/machine/include/lmp-factory-custom.inc, replacing the values for your user and password as appropriate:

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

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.