File: making-systemd-units-wait-for-devices.md

package info (click to toggle)
liquidctl 1.16.0-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 3,452 kB
  • sloc: python: 15,304; sh: 712; xml: 84; makefile: 4
file content (55 lines) | stat: -rw-r--r-- 3,106 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Making systemd units wait for devices

When using a systemd service to configure devices at boot time, as suggested in [Automation and running at boot/Set up Linux using system](../../README.md#set-up-linux-using-systemd), it can sometimes happen that the hardware is not ready when the service tries to start.

A blunt solution to this is to add a small delay, but a more robust alternative is to make the service unit depend on the corresponding hardware being available at the OS level.

## Systemd device units

For this it is first necessary to set up systemd to create device units with known names.  This is done with udev rules, specifically with `TAG+="systemd"` (to create a device unit) and a memorable `SYMLINK+="<some-name>"` name.

```
# /etc/udev/rules.d/99-liquidctl-custom.rules
# Example udev rules to create device units for some specific liquidctl devices.

# create a dev-kraken.device for this third-generation Kraken X
ACTION=="add", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e71", ATTRS{idProduct}=="170e", ATTRS{serial}=="<serial number>", SYMLINK+="kraken", TAG+="systemd"

# create a dev-clc120.device for this EVGA CLC
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="2433", ATTRS{idProduct}=="b200", SYMLINK+="clc120", TAG+="systemd"
```

Setting a custom name with `SYMLINK` is optional: just with `TAG+="systemd"` alone a device unit will be made available as `dev-bus-usb-<bus>-<device>.device`, where the `<bus>` and the `<device>` numbers can be found with the `lsusb` command.

## Setting the dependencies

The new device units can then be added as dependencies to the service unit.

```
# /etc/systemd/system/liquidcfg.service
[Unit]
Description=AIO startup service
Requires=dev-kraken.device
Requires=dev-clc120.device
After=dev-kraken.device
After=dev-clc120.device
...
```

With these changes in place, and after rebooting the system, the service should begin to wait for the devices before trying to starting.

Notes:

- the `SUBSYSTEM` value must match how liquidctl connects to the device; devices listed by liquidctl as on a `hid` bus should use the value `hidraw`, while the remaining should use `usb`
- when possible it is good to include the serial number in the match, to account for the possibility of multiple units of the same model
- on the service unit file `Requires=` is used instead of `Wants=` because we want a [strong dependency](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#%5BUnit%5D%20Section%20Options)
- rebooting the system is not technically necessary, but triggering the new udev rules without a reboot is outside the scope of this document
- some devices may still not be able to response just after being discovered by udev, in which case a delay is really necessary

## Alternative approach

An alternative approach is to have systemd start the configuration service when the device is found by udev, by making the device depend on the service:

```
ACTION=="add", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e71", ATTRS{idProduct}=="170e", ATTRS{serial}=="<serial number>" ENV{SYSTEMD_WANTS}="liquidcfg.service"
```