How can I start a systemd service when a given USB device (ethernet dongle) is plugged in?

8,050

Solution 1

I've seen "How to write a systemd service that depends on a device being present?" but that doesn't seem to be about plugging it in, just about it being present.

It's very similar, only you need the opposite dependency: instead of (or perhaps in addition to) your service depending on the device, you want the virtual device unit to depend on your service.

In fact, despite the linked question's title seemingly asking the opposite thing, the accepted answer in the thread already documents one of the exact methods that would make the device start a service (the udev-rule method).

But if you have an exact .device unit name, it's simpler to use systemd dependencies directly:

If the exact sysfs path is known:

For example, you have determined that the device is sys-subsystem-net-devices-usb0.device. You can use various ways of expanding that unit (despite it being virtual), such as foo.device.d/*.conf drop-ins or foo.device.wants/ symlinks (which work the same way as e.g. multi-user.target.wants/).

  • You can add an [Install] configuration in your service, then systemctl enable it:

    [Install]
    WantedBy=sys-subsystem-net-devices-%i.device
    
  • Or you can get the same result directly with systemctl add-wants:

    # systemctl add-wants sys-subsystem-net-devices-usb0.device [email protected]
    
  • Both methods just result in a symlink that can be made by hand via ln -s.

This generally works the same for user units.

If only partial name (or some combination of other properties) is known:

Write a udev rule that matches your device (the exact udev rule syntax is out of scope here) and have it set the SYSTEMD_WANTS udev property (aka "device environment variable") indicating your unit:

ACTION=="add", SUBSYSTEM=="net", KERNEL=="usb*", ENV{SYSTEMD_WANTS}+="netctl-ifplugd@%k.service"

As all *.rules files are processed in alphabetical order, make sure your rule is placed later than systemd's own "persistent network interfaces" rules.

This works with user units if you use ENV{SYSTEMD_USER_WANTS}.


You might be tempted to use RUN+="/bin/systemctl start foo" via udev. Don't do that. But if you do, then at least use the systemctl --no-block option so that you won't create a possible deadlock between the two components.

Solution 2

udev is what you want to look at. It will allow you to do things like execute commands when hardware is plugged in, unplugged, state changes happen etc. You can also specify a wide variety of criteria to control triggering (i.e. when any storage device is plugged in or just a particular model from a particular manufacturer etc.) You should see any existing rules on your system in /etc/udev/rules.d/

Share:
8,050

Related videos on Youtube

SoniEx2
Author by

SoniEx2

Updated on September 18, 2022

Comments

  • SoniEx2
    SoniEx2 over 1 year

    I have an USB ethernet dongle I use for ethernet access, but I'd like to automatically start netctl-ifplugd@... when plugging it in. (Not to be confused with plugging in the ethernet cable - that's handled by netctl-ifplugd.) How can I make this happen?

    I've seen How to write a systemd service that depends on a device being present? but that doesn't seem to be about plugging it in, just about it being present.

  • Stephen Smith
    Stephen Smith over 4 years
    I'm trying to solve this same problem using the add-wants systemd command - I just got a new ethernet adapter ens4u1u2 so I enabled [email protected] and then tried to add-wants using sudo systemctl add-wants sys-subsystem-net-devices-ens4u1u2.device [email protected], but I get Failed to add dependency: Unit file sys-subsystem-net-devices-ens4u1u2.device does not exist.. Running systemctl status sys-subsystem-net-devices-ens4u1u2.device shows that the device is active but it won't accept it for add-wants. Enabling the .device gives the same error.
  • user1686
    user1686 over 4 years
    If systemd no longer allows this in add-wants, then just create the symlink manually at /etc/systemd/system/sys-....device.wants/, or use the udev rule method (which might be preferred).
  • Stephen Smith
    Stephen Smith over 4 years
    Thanks, I got the udev method to work. For posterity, what I ended up with is ACTION=="add", SUBSYSTEM=="net", KERNEL=="usb*", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="8153", ENV{SYSTEMD_WANTS}+="[email protected]", using values from lsusb