liquidctl/docs/linux/making-systemd-units-wait-f...

3.0 KiB

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, 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
  • 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"