I have written a daemon that manages a number of communication protocols over one, or multiple RS232 devices (typically FT232R usb2serial). Once a /dev/ttyUSB* device appears, if certain attributes are detected, systemd is told to start that daemon via udev:
ENV{SYSTEMD_WANTS}="%s{manufacturer}.service"
Once the daemon has started, it needs to be told which device to open, which I do via udev:
RUN="/usr/bin/sercomc open %E{sd_name} %E{sd_proto} %N"
So the complete udev rule reads like:
ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{manufacturer}=="sercomd", ENV{SYSTEMD_WANTS}="%s{manufacturer}.service", ENV{sd_proto}="%s{product}", ENV{sd_name}="%s{serial}", RUN="/usr/bin/sercomc open %E{sd_name} %E{sd_proto} %N"
Now the problem is that RUN is executed before the daemon is started so that this command obviously has no effect:
systemd-udevd[1638]: starting '/usr/bin/sercomc open ctl-vk1 ctserial /dev/ttyUSB0'
systemd-udevd[1632]: '/usr/bin/sercomc open ctl-vk1 ctserial /dev/ttyUSB0'(err) 'Couldn't connect to server: Connect failed: Connection refused'
[...]
sercomctl[1639]: [2015-10-12 03:05:39:291634] Serial communication daemon ver. 0.5 starting up
Is there a recommended way in resolving this, i.e. trigger running the command once that systemd has finished starting the service?
If you've stumbled upon a similar problem and read the question, don't let yourselves be confused by the name of my software in use. Let it be quickly said that "sercomd" is the name of the daemon that manages the serial connections and "sercomc" is a client program that tells sercomd to open that device using a certain protocol. Furthermore, I have manipulated the EEPROM of the FTDI usb2serial chip such that product, manufacturer and serial strings display user-defined values which I can use for "plug and play" autorecognition of serial adapters.
What I'm doing is now using an instanciated service with unit file /lib/systemd/system/sercomd@.service for every device that appears.
The udev rule now reads:
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{manufacturer}=="sercomd", ACTION=="add", SYMLINK="tty%s{serial}", PROGRAM="/bin/systemd-escape %s{serial}", ENV{SYSTEMD_WANTS}="sercomd@%c.service", ENV{sd_proto}="%s{product}", ENV{sd_dev}="%N"
This rule sets a few environment variables sd_proto and sd_dev, and the third parameter sd_dev which can then be extracted from the sercomd@.service file:
ExecStart=/bin/bash -c "eval $$( udevadm info --query=env --export /dev/tty%I ); sercomc open %I $$sd_proto $$sd_dev"
ExecStop=/usr/bin/sercomc -l %I close
Thus every time a device is added or removed from the USB hub, the appropriate client command is run from systemd. By adding
Requires=sercomd.service
After=sercomd.service
to the unit file, it will also make sure my daemon that actually manages these interfaces is started before the client gets run.