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:** .. code-block:: bash [Unit] Description="Our backend that provides data" [Service] Type=simple ExecStart=/usr/bin/launch-backend.sh [Install] WantedBy=multi-user.target **user.app.serivce:** .. code-block:: bash [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 backend 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: .. code-block:: bash 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. .. code-block:: bash [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 to launch until 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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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 the service's dependency tree. To find out what services depend on a specific one, use the -reverse -flag: .. code-block:: bash 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 to see when 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 a device is not visible it can be manually mapped. For this example the light sensor that is located on the i2c-bus is used, but also as a symlink on /dev/lightsensor. First check the details of the device with udevadm: .. code-block:: bash 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: .. code-block:: bash SUBSYSTEM=="i2c", KERNELS=="16-0044", TAG="systemd", ENV{SYSTEMD_ALIAS}="/dev/lightsensor" Save the file, and reload udev: .. code-block:: bash udevadm control --reload-rules && udevadm trigger The device will now be visible as a systemd.device: .. code-block:: bash 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, the tags added with udevadm are now visible: .. code-block:: bash 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: ------------------------------------------------------------------ 9.3.1 Launching services from udev (not recommended) ------------------------------------------------------------------ To launch services directly from udev rules add the necessary service to the rule. .. code-block:: bash ENV{SYSTEMD_WANTS}+="lightsensor.service" This will run the specific service when the device is connected. A drawback of this method is that if the service is enabled elsewhere, it is difficult to know where it was launched from. Thus, the suggested method is to run all services from targets.