Create a systemd service#

The SimpleCore™ distribution use systemd as init and service manager. There is the official documentation of systemd project here.

systemd units are the way to described a part of the system which is managed by systemd. Hereafter in this article, it will be explained how to add a systemd service, the particular type of systemd unit to manage a process.

Directly on the target#

Out of a package system, a good place to create the systemd service in the Linux file hierarchy is /etc/systemd/system. The name of the systemd service must be composed by:

  • a prefix, the name of the systemd service using ASCII letters, digits, “:”, “-”, “_”, “.”, and “".

  • the suffix “.service”.

In that file, there is generally the following two section:

  • the Unit section, which contains information not related to a particular type of systemd unit like a description, the dependencies of the unit…

  • the Service section which describes the information about the process.

Here is a small example of a simple service, just printing Hello World. The content of the service file /etc/systemd/system/hello_world.service is

[Unit]
Description=Hello World

[Service]
ExecStart=/bin/echo "Hello World"

It is possible to start that service using the command

$ systemctl start hello_world

And to check the status of the process thanks to

$ systemctl status hello_world

It should display

* hello_world.service - Hello World
     Loaded: loaded (/etc/systemd/system/hello_world.service; static)
     Active: inactive (dead)


Jun 21 13:50:24 sm2s-imx8mini systemd[1]: Started Hello World.
Jun 21 13:50:24 sm2s-imx8mini echo[815]: Hello World
Jun 21 13:50:24 sm2s-imx8mini systemd[1]: hello_world.service: Deactivated successfully.

Moreover, if the systemd service must be executed automatically on boot, an Install section is added to describe when the service must be started. The example becomes

[Unit]
Description=Hello World

[Service]
ExecStart=/bin/echo "Hello World"

[Install]
WantedBy=multi-user.target

Then it is possible to enable the service, to indicate that it should start on boot, thanks to the command

$ systemctl enable hello_world.service

The command should create a symbolic link in the drop-in directory of the indicated target. A target is a type of systemd unit to create synchronization point, grouping systemd units. Here you should see

$ systemctl enable hello_world.service
Created symlink /etc/systemd/system/multi-user.target.wants/hello_world.service -> /etc/systemd/system/hello_world.service.

If you reboot the board and display the status of the service, you should have

* hello_world.service - Hello World
     Loaded: loaded (/etc/systemd/system/hello_world.service; enabled; vendor preset: disabled)
     Active: inactive (dead) since Wed 2023-06-21 15:11:16 UTC; 16s ago
    Process: 496 ExecStart=/bin/echo Hello World (code=exited, status=0/SUCCESS)
   Main PID: 496 (code=exited, status=0/SUCCESS)

Jun 21 15:11:16 sm2s-imx8mini systemd[1]: Started Hello World.
Jun 21 15:11:16 sm2s-imx8mini echo[496]: Hello World
Jun 21 15:11:16 sm2s-imx8mini systemd[1]: hello_world.service: Deactivated successfully.

The default target can be known thanks to the command

$ systemctl get-default

Generally it would be:

  • multi-user.target

  • graphical.target

And one of this target should be a good target for a user application. If you need to start your application earlier, it is possible to display all the target thanks to

$ systemctl list-units *target

With a Yocto recipe#

If a recipe is already created to make a package of your application you must add in that recipe:

  • An inherit systemd to use the systemd class.

  • Install the service file in the correct directory. ${D}${systemd_system_unitdir} should be a good place (it will end up in /lib/systemd/system on the target).

  • Define the variable SYSTEMD_PACKAGES. By default it is set to ${PN} so if your systemd unit must go in that package, you haven’t to define that variable.

  • Define the variable SYSTEMD_SERVICE. It is the name of the systemd service found in the package (so a package override must be used with this variable).

  • Add the systemd service to the FILES that are placed in a package.

So if we want to create a recipe to add the previous hello-world.service we could write

# SPDX-FileCopyrightText: (C) 2022 Avnet Embedded GmbH
# SPDX-License-Identifier: LicenseRef-Avnet-OSS-1.0
SUMMARY = "Hello World"
LICENSE = "Avnet-OSS-1.0"
LIC_FILES_CHKSUM = "file://${WORKDIR}/LICENSE;md5=4e8c3f6a224c21c1323ae04587ba060c"

SRC_URI = " \
    file://LICENSE \
    file://hello-world.service \
"

inherit allarch systemd

# No need to define SYSTEMD_PACKAGES as it is set by default to "${PN}"
SYSTEMD_SERVICE:${PN} = "hello-world.service"

do_configure[noexec] = "1"
do_compile[noexec] = "1"

do_install () {
    install -d ${D}${systemd_system_unitdir}
    install -m 0644 ${WORKDIR}/hello-world.service ${D}${systemd_system_unitdir}/hello-world.service
}

FILES:${PN} = "${systemd_system_unitdir}/hello-world.service"

By default the service will be automatically started on boot. It is possible to disable it, setting in the recipe the variable SYSTEMD_AUTO_ENABLE to disable.