Bug 871074 - systemd ignores udev remove rules
Summary: systemd ignores udev remove rules
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: systemd
Version: 18
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: systemd-maint
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2012-10-29 14:43 UTC by Marko Myllynen
Modified: 2012-10-31 14:05 UTC (History)
9 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed: 2012-10-29 15:25:22 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)

Description Marko Myllynen 2012-10-29 14:43:06 UTC
Description of problem:
With /etc/udev/rules.d/99-test.rules containing:

SUBSYSTEM!="usb", GOTO="test_usb_rules_end"

ACTION=="add",    ENV{DEVTYPE}=="usb_device", RUN+="/tmp/test.bash run-add"
ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="/tmp/test.bash run-remove"

ACTION=="add",    ENV{DEVTYPE}=="usb_device", TAG+="systemd", ENV{SYSTEMD_WANTS}="test-add.service"
ACTION=="remove", ENV{DEVTYPE}=="usb_device", TAG+="systemd", ENV{SYSTEMD_WANTS}="test-remove.service"

LABEL="test_usb_rules_end"

And /tmp/test.bash containing:

#!/bin/bash
echo $0 $@ called by $PPID >> /tmp/test.log

One can see from /tmp/test.log that when adding/removing a USB device the RUN rules are executed as expected but only the test-add.service is invoked with systemd even though udevadm monitor --environment --udev prints the expected SYSTEMD_WANTS/TAGS variables when a USB device is removed.

The systemd service files used are like:

[Unit]
Description=Test Add

[Service]
ExecStart=/tmp/test.bash systemd-add

And:

[Unit]
Description=Test Remove

[Service]
ExecStart=/tmp/test.bash systemd-remove


After few times adding/removing a USB device, /tmp/test.log looks something like:

/tmp/test.bash run-add called by 19250
/tmp/test.bash systemd-add called by 1
/tmp/test.bash run-remove called by 19262
/tmp/test.bash run-add called by 19271
/tmp/test.bash systemd-add called by 1
/tmp/test.bash run-remove called by 19283


Version-Release number of selected component (if applicable):
systemd-195-2.fc18.x86_64

Comment 1 Michal Schmidt 2012-10-29 15:05:49 UTC
It is important to realize how "TAG+="systemd", ENV{SYSTEMD_WANTS}=..." works.

The "systemd" tag tells systemd to instantiate a *.device unit to match the device as seen by udev. The unit will be in the "active (plugged)" state as long as the device is present.

"ENV{SYSTEMD_WANTS}=foo" tells systemd that the *.device unit should have "Wants=" dependency on "foo". As the *.device unit is active, systemd will fulfill this requirement dependency.

When the device goes away, it disappears from udev database and systemd stops the *.device unit. Since the device is no more in the database, there's nothing to attach the SYSTEMD_WANTS property to.

I don't think it ever makes sense to set ENV{...} on a device that's being removed.

Kay, am I right?

Comment 2 Michal Schmidt 2012-10-29 15:14:01 UTC
Here are two possible workarounds:

1. Use a service with "StopWhenUnneeded=yes". When the device gets unplugged, your service will be stopped. You can take some action in ExecStop.

2. Somewhat more hacky: Run "systemctl start --no-block test-stop.service" from the udev remove rule.

Comment 3 Kay Sievers 2012-10-29 15:16:54 UTC
Sounds right. I think we can not start stuff for units that are gone, or
are about to go. Systemd wants the device to pull something in, when it's
gone, in the current model, we can not instantiate in the context that is
no more.

Comment 4 Kay Sievers 2012-10-29 15:24:10 UTC
(In reply to comment #2)
> Here are two possible workarounds:
> 
> 1. Use a service with "StopWhenUnneeded=yes". When the device gets
> unplugged, your service will be stopped. You can take some action in
> ExecStop.

Right, it generally sounds more fitting to instantiate something ad "add",
and let systemd track this unit, and remove exactly this same unit
at "remove", instead of creating a new unit, unrelated to the first one,
and try to match them together.

Comment 5 Michal Schmidt 2012-10-29 15:25:22 UTC
OK, I'm closing this as NOTABUG. Let us know if for whatever the reason the provided solution in comment #2 does not work for you.

Comment 6 Marko Myllynen 2012-10-31 13:13:58 UTC
> 1. Use a service with "StopWhenUnneeded=yes". When the device gets
> unplugged, your service will be stopped. You can take some action in
> ExecStop.
> 
> 2. Somewhat more hacky: Run "systemctl start --no-block test-stop.service"
> from the udev remove rule.

Thanks, it was possible to retain the application unchanged by using RUN+="/bin/systemctl --no-block reload-or-restart test-{add,remove}.service" in the udev rules and defining ExecStart/ExecReload/Type/RemainAfterExit in the service files.

Comment 7 Michal Schmidt 2012-10-31 13:30:30 UTC
Marko,
may I know what application it is? It looks like something that would benefit from subscribing to udev events by itself using libudev.

Comment 8 Marko Myllynen 2012-10-31 14:05:24 UTC
(In reply to comment #7)
> Marko,
> may I know what application it is? It looks like something that would
> benefit from subscribing to udev events by itself using libudev.

Thanks for the suggestion, we might investigate that at some point in the future - it's an in-house application running on RHEL/Fedora/Ubuntu at a customer, it interacts with the user after device insertion/removal and then, depending on the user input, proceed changing the state of the system quite drastically. Feel free to ping me over IRC if you want more details.


Note You need to log in before you can comment on or make changes to this bug.