9. Working with Systemd

The basics of creating, configuring, and starting systemd services are explained in the software guide. This section provides a more detailed look in to how systemd can be configured in special cases i.e when applications and services need to be tied to specific devices or other services.

9.1 Creating services with dependencies

Sometimes services can include dependencies to other services and a requirement might be that these services need to be executed in a specific order. Additionally, services may require a specific device to be operational before the service can be started.

9.1.1 Example: service that depends on other services

Assume you have a backend.service that provides connectivity and a userapp.service that provides the user interface. Thus, you want to make sure that the backend is running before the user application is started.

backend.service:

[Unit]
Description="Our backend that provides data"

[Service]
Type=simple
ExecStart=/usr/bin/launch-backend.sh

[Install]
WantedBy=multi-user.target

user.app.serivce:

[Unit]
Description="User interface to view data"
After=backend.service
Requires=backend.service

[Service]
Type=simple
ExecStart=/usr/bin/launch-gui.sh

[Install]
WantedBy=multi-user.target

Finally, enable the service with systemctl enable user.app.service. This will automatically load the backend also.

9.1.2 Example: service that depends on a specific device

Assume that your backed provides data from a can-bus, and thus requires the driver to be loaded before starting the service.

First find out the .device that you want to bind the service to:

ccs@v700:~# sudo systemctl --type=device --all|grep can0
sys-subsystem-net-devices-can0.device
loaded active plugged /sys/subsystem/net/devices/can0
ccs@v700:~#

Then, add the binding to the service that requires the specific interface.

[Unit]
Description="Our backend that provides data from CAN0"
BindsTo=sys-subsystem-net-devices-can0.device
After=sys-subsystem-net-devices-can0.device

[Service]
Type=simple
ExecStart=/usr/bin/launch-backend.sh

Now, your service will be bound to wait after the specific device driver has been loaded.

If you cannot find the device, you will need to add a specific udev-rule, creating a .device node with system. See 9.3

9.1.3 Service file locations

For systemd to find service files they must be located in predefined locations. These locations can be checked for each system with the following commands.

For user files:

ccs@v700:~# sudo systemd-analyze --user unit-paths
…
/home/root/.config/systemd/user.control
/home/root/.config/systemd/user
/etc/systemd/user
…

For system related files:

ccs@v700:~# sudo systemd-analyze --system unit-paths
…
/etc/systemd/system
/usr/local/lib/systemd/system
/lib/systemd/system
/usr/lib/systemd/system
…

9.2 Viewing dependencies with systemctl list-dependencies

Finding out dependencies might become cumbersome at times and systemd provides tools to help out in this task. It is possible to view dependencies for each service, but also in the reverse.

As an example the System Supervisor runs a specific service on the device on each boot. To see the dependencies for this, run:

ccs@v700:~# sudo systemctl list-dependencies --all ss.service
ss.service
● ├─system.slice
● └─sysinit.target
●   ├─dev-hugepages.mount
●   ├─dev-mqueue.mount
●   ├─kmod-static-nodes.service
●   ├─ldconfig.service
●   ├─psplash-start.service
●   ├─psplash-systemd.service
●   ├─run-postinsts.service
●   ├─sys-fs-fuse-connections.mount
●   ├─sys-kernel-config.mount
●   ├─sys-kernel-debug.mount
●   ├─sys-kernel-tracing.mount
●   ├─systemd-ask-password-console.path
●   ├─systemd-hwdb-update.service
●   ├─systemd-journal-catalog-update.service
●   ├─systemd-journal-flush.service
●   ├─systemd-journald.service
●   ├─systemd-machine-id-commit.service
●   ├─systemd-modules-load.service
●   ├─systemd-network-generator.service
●   ├─systemd-pstore.service
●   ├─systemd-sysctl.service
●   ├─systemd-sysusers.service
●   ├─systemd-tmpfiles-setup-dev.service
●   ├─systemd-tmpfiles-setup.service
●   ├─systemd-udev-trigger.service
●   ├─systemd-udevd.service
●   ├─systemd-update-done.service
●   ├─systemd-update-utmp.service
●   ├─local-fs.target
●   │ ├─systemd-remount-fs.service
●   │ ├─tmp.mount
●   │ ├─var-volatile-cache.service
●   │ ├─var-volatile-lib.service
●   │ ├─var-volatile-spool.service
●   │ ├─var-volatile-srv.service
●   │ └─var-volatile.mount
●   └─swap.target

This will provide you with a dependency tree of the service.

To find out what services depend on a specific one, use the -reverse -flag:

ccs@v700:~# sudo systemctl list-dependencies ss.service --all --reverse
ss.service
● ├─ccauxd.service
● │ └─multi-user.target
● │   └─graphical.target
● └─sys-module-ss.device

The reverse depends will help you to see, where a specific service is started if it is not listed in any of the targets (but is launched as a dependency from another .service).

9.3 Adding systemd .devices

By default, systemd maps most of the devices (all tagged with “systemd”). However if the device is not visible you may create a mapping to use. For this example, let’s use the light sensor that is located on the i2c-bus, but also as a symlink on /dev/lightsensor.

First use udevadm to check the details of the device:

ccs@v700:~# sudo udevadm info /sys/devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044
P: /devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044
L: 0
E: DEVPATH=/devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044
E: DRIVER=isl29018
E: OF_NAME=isl29035
E: OF_FULLNAME=/bus@5a000000/i2c@5a810000/isl29035@44
E: OF_COMPATIBLE_0=isil,isl29035
E: OF_COMPATIBLE_N=1
E: MODALIAS=of:Nisl29035T(null)Cisil,isl29035
E: SUBSYSTEM=i2c
E: USEC_INITIALIZED=4914427

Then, create a rule in udev, to add the tag “systemd” to the device, and also map the /dev/lightsenor link to make it more useable.

Under /etc/udev/rules.d/lightsensor.rules, add:

SUBSYSTEM=="i2c", KERNELS=="16-0044", TAG="systemd",
ENV{SYSTEMD_ALIAS}="/dev/lightsensor"

Save the file, and reload udev:

udevadm control --reload-rules && udevadm trigger

Device will now be visible as a systemd.device:

ccs@v700:~# sudo systemctl --type=device --all |grep -e 044 -e lightsensor
dev-lightsensor.device
loaded active plugged /dev/lightsensor
sys-devices-platform-bus\x405a000000-5a810000.i2c-i2c\x2d16-16\x2d0044.device
loaded active plugged /sys/devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044

Additionally, you will be able to see the added tags with udevadm:

ccs@v700:~# sudo udevadm info /sys/devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044
P: /devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044
L: 0
E: DEVPATH=/devices/platform/bus@5a000000/5a810000.i2c/i2c-16/16-0044
E: DRIVER=isl29018
E: OF_NAME=isl29035
E: OF_FULLNAME=/bus@5a000000/i2c@5a810000/isl29035@44
E: OF_COMPATIBLE_0=isil,isl29035
E: OF_COMPATIBLE_N=1
E: MODALIAS=of:Nisl29035T(null)Cisil,isl29035
E: SUBSYSTEM=i2c
E: USEC_INITIALIZED=4914427
E: SYSTEMD_ALIAS=/dev/lightsensor
E: TAGS=:systemd: