每当插入 SD 卡时,我都想使用自定义脚本(写入分区表,然后复制 rootfs 等)刷新 SD 卡。
乍一看,这似乎很简单——我只需使用 udev 规则来检测 SD 卡,然后调用脚本即可。第一个问题是,由于读卡器已经存在,它不会触发“添加”,只会触发“更改”,不仅如此,它还会引发多个事件并多次触发脚本。经过更多过滤后,我能够让一个简单的脚本仅触发一次(/etc/udev/rules.d/99-sd-detect.rules):
ACTION=="change", KERNEL=="sd[b-z]", SUBSYSTEM=="block", ENV{DISK_MEDIA_CHANGE}=="1", ENV{DEVTYPE}=="disk", ATTRS{idVendor}=="8564", ATTRS{idProduct}=="4000", ENV{ID_DRIVE_FLASH_CF}=="1", RUN+="/bin/sh -c '/root/udev_flash_sd.sh %E{DEVNAME}'"
但是,当我实际让脚本将数据写入 SD 卡时,它会在几秒钟后被终止。显然,在 2012 年,udev 进行了更改,终止了几秒钟后没有返回的进程。我尝试了各种分叉/分离/nohup 等方法,试图让它继续运行,但无济于事。
似乎正确的解决方案是让它调用 systemd 服务来运行任何需要更长时间的操作,例如:
ACTION=="change", KERNEL=="sd[b-z]", SUBSYSTEM=="block", ENV{DISK_MEDIA_CHANGE}=="1", ENV{DEVTYPE}=="disk", ATTRS{idVendor}=="8564", ATTRS{idProduct}=="4000", ENV{ID_DRIVE_FLASH_CF}=="1", TAG+="systemd", PROGRAM="/bin/systemd-escape -p [email protected] $env{DEVNAME}", ENV{SYSTEMD_WANTS}+="%c"
其中 /etc/systemd/system/[电子邮件保护]有:
[Unit]
BindTo=%i.device
After=%i.device
[Service]
Type=oneshot
TimeoutStartSec=360
ExecStart=/root/udev_flash_sd.sh /%I
如果您调用“systemctl start”,则此方法有效[电子邮件保护]“,这也是 udev 规则的 PROGRAM 部分输出的内容(至少对于 /dev/sde 而言)。经过大量搜索,它看起来像是 udev将不会在“更改”事件上调用服务,仅“添加”。 (“udevadm monitor -p”正确显示SYSTEMD_WANTS和标签,更不用说测试调用systemd服务的简单添加规则工作正常。)
那么,如何在插入 SD 卡时自动调用自定义刷新脚本?
答案1
编辑:似乎最好的方法是将命令通过管道传送到“at now”,以防止 udev 将其杀死。例如,“echo mycommand.sh | at now”。
最初看起来“& disown”似乎可以起作用,但实际上它遇到了与 udev 杀死它相同的问题,就在 300 秒(5 分钟)之后,而不是~2-3 秒。
答案2
这不是答案,但您的规则仅适用于添加而不适用于更改的原因是systemd
选择实施该限制。例如,参见systemd.device
手册页,其中写道:
请注意,systemd 仅在设备首次处于活动状态时对 Wants= 依赖项采取行动。如果将它们添加到已处于活动状态的设备,它将不会对它们采取行动。使用 SYSTEMD_READY=(见下文)来配置何时应将 udev 设备视为活动设备,从而配置何时触发依赖项。
换句话说,这SYSTEMD_WANTS
不是一个让 systemd 在发生服务时启动服务的指令,它只是Wants
向自动生成的设备单元添加一个指令,并且Wants
指令只有在单元被激活时才会生效。
该手册页还建议使用它SYSTEMD_READY
来控制设备何时处于活动状态,但我认为这需要先将其设置为 0 然后再设置回 1,这可能需要 2 个单独的 udev 事件(不确定您的设备是否会在拔出 SD 卡时发出事件)。