为什么 Systemd_Wants 不将参数从 udev“删除”规则传递到服务文件?

Fle*_*phy 1 usb mount udev systemd

我正在尝试使用 udev 规则和 systemd 来安装和卸载 USB 闪存盘。

我的解决方案基于此博客中的一个示例,其中 ENV{SYSTEMD_WANTS} 用于“添加”udev 规则。我无法让 ENV{SYSTEMD_WANTS} 用于“删除”规则,因此我最终使用了此博客文章中的示例,该示例从 udev RUN 键运行 systemd 服务文件。

尽管此解决方案有效,但udev 手册页显示:“长时间运行事件进程可能会阻止该设备或相关设备的所有进一步事件。”

这些是我使用的规则...

添加规则:

ACTION=="add", SUBSYSTEM=="block", ENV{ID_TYPE}="disk", \ 
ENV{DEVTYPE}=="partition", ENV{ENV_MOUNT_USB}="MOUNT", \ 
PROGRAM="/usr/bin/systemd-escape -p --template=usb-mount@.service \ 
$env{DEVNAME}", ENV{SYSTEMD_WANTS}+="%c"
Run Code Online (Sandbox Code Playgroud)

删除规则:

ACTION=="remove", SUBSYSTEM=="block", ENV{ID_TYPE}="disk", \ 
ENV{DEVTYPE}=="partition", RUN+="/bin/systemctl start usb-umount@%k.service"
Run Code Online (Sandbox Code Playgroud)

这些是 udev 规则启动的 systemd 服务文件。他们只需执行 bash 脚本来挂载和卸载 USB 闪存盘。

usb-mount@.service

[Unit]
Description=Mount USB Drive on %i
BindTo=%i.device
After=%i.device

[Service]
Type=oneshot
TimeoutStartSec=300
ExecStart=/usr/local/sbin/usb-mount.sh /%I
Run Code Online (Sandbox Code Playgroud)

usb-umount@.service

[Unit]
Description=UnMount USB Drive on %i

[Service]
Type=oneshot
TimeoutStartSec=300
ExecStart=/usr/local/sbin/usb-umount.sh /%I
Run Code Online (Sandbox Code Playgroud)

我的问题
但是,如果我在“删除”规则中使用 ENV{SYSTEM_WANTS},则分离 USB 密钥的设备块(例如:/dev/sdb1)不会传递到服务文件。实际上我认为服务文件根本没有运行。

我的问题
我的删除规则是否可以更改为与 ENV{SYSTEM_WANTS} 和 NOT RUN+={..} 一起使用,以便将 /dev/sdb1 传递到服务文件并传递到可以执行卸载的脚本?

干杯,

柔性

Fle*_*phy 5

经过大量调查,我对上述问题有了答案。

\n

问题说明

\n

关键点在Systemd 设备手册页中中:

\n
\n

\xe2\x80\xa2 (在 udev 规则中) "Systemd_wants="... "将类型\nWants=从设备单元(例如:USB 密钥)到指定单元\n(即:systemd 服务)的依赖项添加单元)。”

\n

\xe2\x80\xa2 \xe2\x80\x9c...当设备\n首次激活时,systemd 仅会对 Wants= 依赖项起作用。\xe2\x80\x9d

\n
\n

我通过插入和拔出 USB 闪存盘进行测试。测试发现这个 udev 规则不起作用:

\n
ACTION=="remove", SUBSYSTEM=="block", ENV{ID_TYPE}="disk", \\\nENV{DEVTYPE}=="partition", ENV{SYSTEMD_WANTS}+="usb-umount@%k.service"\n
Run Code Online (Sandbox Code Playgroud)\n

我是这样调试的:

\n
sudo udevadm control --log-priority=debug\njournalctl -f\n
Run Code Online (Sandbox Code Playgroud)\n

当我移除已插入的 USB 密钥时,我没有在journalctl。因此,该服务文件甚至不会启动,这是有意义的,因为手册页中的内容(见上文)。

\n

相反,当我将规则更改为:

\n
ACTION=="remove", SUBSYSTEM=="block", ENV{ID_TYPE}="disk", \\\nENV{DEVTYPE}=="partition", RUN+="/bin/systemctl start usb-umount@%k.service"\n
Run Code Online (Sandbox Code Playgroud)\n

现在在journalctl输出中我看到:

\n
sdb1: Starting \'/bin/systemctl start usb-umount@sdb1.service\'\n
Run Code Online (Sandbox Code Playgroud)\n

关于为什么 Systemd_Wants 在这个 Reddit 线程中的 udev 删除规则中不起作用有一个很好的讨论中的 udev 删除规则中工作,有一个很好的讨论。

\n

引用该 Reddit 主题:

\n
\n

\xe2\x80\x9cSYSTEMD_WANTS 和 SYSTEMD_USER_WANTS 对于\nACTION==“删除”没有意义。看起来这些变量可能使 Udev 要求 systemd 执行某些操作,但实际上关系是相反的:当设备单元添加到 systemd 管理器时,systemd 从 Udev 检索该变量。在任何其他时间\n都不会检索该变量。\xe2\x80\x9d

\n
\n

这也解释了为什么 Systemd_Wants 不使 usb-mount 服务根据 udev“删除”规则运行。

\n

新的解决方案

\n

该 Reddit 线程还展示了一种通过 udev \xe2\x80\x9cremove\xe2\x80\x9d 操作使用 systemd_wants 的迂回方式:

\n

现在,要使用 udev 来响应从我的机器上插入和拔出 USB 密钥,我只需要一条 udev 规则,灵感来自于 Reddit 帖子和此博客

\n

udev 规则
\n\xe2\x80\xa2 /etc/udev/rules.d/99-local.rules

\n
ACTION=="add", SUBSYSTEM=="block", ENV{ID_TYPE}="disk", \\\nENV{DEVTYPE}=="partition", ENV{ENV_MOUNT_USB}="USB-MOUNTED", \\\nPROGRAM="/usr/bin/systemd-escape -p --template=test-mount@.service \\ \n$env{DEVNAME}", ENV{SYSTEMD_WANTS}+="%c"\n
Run Code Online (Sandbox Code Playgroud)\n

服务文件
\n\xe2\x80\xa2 /etc/systemd/system/test-mount@.service

\n
[Unit]\nDescription=Test-Mount USB Drive on %i\nBindsTo=%i.device\nAfter=%i.device\n\n[Service]\nType=oneshot\nRemainAfterExit=true\nTimeoutStartSec=300\nExecStart=/usr/local/sbin/test-mount.sh /%I\nExecStop=/usr/local/sbin/test-umount.sh /%I\n
Run Code Online (Sandbox Code Playgroud)\n

我使用的脚本...
\n\xe2\x80\xa2 /usr/local/sbin/test-mount.sh

\n
#!/bin/bash\n\n#\n# This script runs when a USB device is plugged in\n#\n\nDEVBASE=$1\nDEVICE="${DEVBASE}"\n\n# Create a Timestamp, e.g: 06.05.2022-20.11.49\ncurrent_time=$(date "+%d.%m.%Y-%H.%M.%S")\n\n# Query the udev database which is available while the USB key is plugged in.\n# However these udev properties are unavailable once the USB key is plugged out.\neval $(udevadm info --query=env --export $DEVICE)\n\n# Create a temporary file\ntouch /tmp/test-mount.txt\n\n# Add content to the file\necho ${current_time} > "/tmp/test-mount.txt"\necho ${DEVICE} >> "/tmp/test-mount.txt"\necho $ID_FS_LABEL  >> "/tmp/test-mount.txt"\necho $ENV_MOUNT_USB  >> "/tmp/test-mount.txt"\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa2 /usr/local/sbin/test-umount.sh

\n
#!/bin/bash\n\n#\n# This script runs when a USB device is plugged out\n#\n\nDEVBASE=$1\nDEVICE="${DEVBASE}"\n\n# Create a Timestamp, e.g: 06.05.2022-20.11.49\ncurrent_time=$(date "+%d.%m.%Y-%H.%M.%S")\n\n# Query the udev database which is available while the USB key is plugged in.\n# However these udev properties are unavailable once the USB key is plugged out.\neval $(udevadm info --query=env --export $DEVICE)\n\n# Create a temporary file\ntouch /tmp/test-umount.txt\n\n# Add content to the file\necho ${current_time} > "/tmp/test-umount.txt"\necho ${DEVICE} >> "/tmp/test-umount.txt"\necho $ID_FS_LABEL  >> "/tmp/test-umount.txt"\necho $ENV_MOUNT_USB  >> "/tmp/test-umount.txt"\n/usr/bin/printenv > /tmp/udev-env.txt\n
Run Code Online (Sandbox Code Playgroud)\n

我用来测试和调试的命令
我用于测试和调试\n\xe2\x80\xa2 插入 USB 和移除 USB 闪存盘的

\n
ls -l /tmp\n
Run Code Online (Sandbox Code Playgroud)\n

像这样调试...
\n\xe2\x80\xa2 这确实是您应该费心测试 udev 规则的唯一方法!
\n\xe2\x80\xa2 运行命令,然后插入和移除 USB 闪存盘。

\n
sudo udevadm control --log-priority=debug\njournalctl -f\n
Run Code Online (Sandbox Code Playgroud)\n

还有这个...

\n
udevadm monitor --subsystem-match=block --property --udev\nudevadm monitor --property --udev\n
Run Code Online (Sandbox Code Playgroud)\n

像这样测试...
\n\xe2\x80\xa2 这个测试不如上面的有用,因为它只模拟 udev 规则做什么!
\n\xe2\x80\xa2 例如:对于 /dev/sdb1,您可以像这样测试 udev 规则:

\n
# sudo udevadm test --action=add /sys/class/block/sdb1\n
Run Code Online (Sandbox Code Playgroud)\n