Linux – the correct way to write a udev rule to stop a service under systemd

linuxsystemdudev

I'm running Arch Linux, and I have a udev rule which starts a service when a device is inserted. In this case, it dials a connection when a 3G modem is plugged in.

KERNEL=="ttyUSB*", SYMLINK=="gsmmodem", TAG+="systemd", ENV{SYSTEMD_WANTS}="netcfg@wvdial.service"

However, if the device is removed, systemd won't stop the service, and hence when it is plugged in again, it won't start the service, since it's already running.

What I need is a matching udev rule which runs when the device is removed to stop the service.

Update

Using the answer below, what I now have is the following udev rule

KERNEL=="ttyUSB*", SYMLINK=="gsmmodem", TAG+="systemd", ENV{SYSTEMD_WANTS}="vodafone.service"

with the following service file (which was basically copied and pasted from the netcfg service file:

[Unit]
Description=Netcfg networking service for Vodafone Dongle
Before=network.target
Wants=network.target
BindsTo=dev-gsmmodem.device
After=dev-gsmmodem.device

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/netcfg check-iface wvdial
ExecStop=-/usr/bin/netcfg down wvdial
KillMode=none

[Install]
WantedBy=multi-user.target

I'm using netcfg-wvdial from the AUR to do the dialing.

Best Answer

Your problem may be solved using systemd solely, by simply specifying that your service Requires or, even better, BindsTo the given device.

Quoting:

"If one of the other [required/bound to] units gets deactivated or its activation fails, this unit [service] will be deactivated"

You just need to edit your service file like the following.

[Unit]
<...>
BindsTo=<DEVICE UNIT HERE>.device
<...>
After=<DEVICE UNIT HERE>.device

Note: to get a list of all available device unit files use systemctl list-units --all --full | grep ".device"

Related Question