How to run long-time process on udev event
I will show the problem, and solution, by example. In EasyOS,
also in all the official woof-CE built pups, there is a rule in
/etc/udev/rules.d/88-puppy-autodetect.rules, with this in it (a bit
different in woof-CE, but same principle):
ACTION=="add", SUBSYSTEM=="usb", ENV{INTERFACE}=="6/1/*", RUN+="/usr/sbin/pupautodetect camera"
Now here is the crux of the problem: the udev daemon is held up until
'pupautodetect' terminates. If 'pupautodetect' takes a long time to
terminate, it can cause serious problem with running of the daemon.
So, we need to get out of 'pupautodetect' as soon as possible. The
script has a 20-second wait loop, which is bad, but that is only one-off
at first pristine bootup. Then, 'pupcamera' is called, like this:
case $1 in
camera)
pupcamera &
;;
...and this is where something happens, or rather doesn't happen,
that took me by surprise -- the "&" does not detach the process!
This is really the "crux of the crux" of the problem!
I wrote about detaching recently:
https://bkhome.org/news/202009/detach-child-process-from-parent.html
All right, as "&", nor "disown $!" work, I tried "setsid --fork"
when I was working on BluePup. I thought that was working, very odd,
because it definitely isn't now. Doing this in 'pupautodetect',
'pupautodetect-run' does not even run:
setsid --fork pupautodetect-run $@
The idea was, I would get out of 'pupautodetect' real quick, then 'pupautodetect-run' can run for as long as it wants.
This problem of detaching a process that is called by a udev rule, is
one that others have encountered, with various solutions, such as here:
https://unix.stackexchange.com/questions/56243/how-to-run-long-time-process-on-udev-event
...the "at now" solution does not appeal to me, besides, don't have the 'at' executable installed.
...the weird thing is, someone has posted that 'setsid' worked. That
is just so weird, because I thought it was working also, pretty sure it
was. Yesterday was testing EasyOS 2.5.2 with the simplified 4.19.161
kernel, and setsid did not work, and I thought that I must have made a
configure change in the kernel that has broken it. So built 2.5.2.1 with
the "full feature" 5.4.81 kernel, same problem. Hmmm...
Could install 'at', but then I remembered busybox's 'cttyhack', that I
have used before. Yes, this works, have this in 'pupautodetect':
cttyhack sh pupautodetect-run $@ &
I can confirm, by running "ps | grep pupautodetect'", that
'pupautodetect' is released, and udevd immediately processes the next
uevent -- which I also confirmed.
I have accordingly fixed these scripts in woofQ:
/usr/sbin/pupautodetect
/usr/local/pup_event/bluetooth-add, bluetooth-remove, bluetoothhw
I will let 01micko know about this, he might want to look at
'pupautodetect', etc., in woof-CE. jamesbond has in-depth knowledge of
udev and how processes work, and might have further thoughts, will
contact him also.
EDIT:
I was very puzzled about that person reporting that 'setsid' works, so I
did it this way, with full path to 'pupautodetect-run', and the script
then did run:
setsid --fork /usr/sbin/pupautodetect-run $@
...however, it did not detach. It still held up
udevd. So what that person reported is incorrect. Really odd that
needed the full path!
But I now understand why 'setsid' seemed to be
working when I was developing BluePup -- I had the full path to the
script. It only seemed to be working though, as although it executed the
script, it did not detach.
EDIT:
A bit more information, that might interest 01micko. As posted recently, I have adapted 'pupmtp' from woof-CE:
https://bkhome.org/news/202012/considering-mtp-access-to-an-android-phone.html
However, now that I have freed udevd, when I
select PTP mode for the USB connection in my phone (old nexus 5,
"Developer options -> Select USB configuration -> PTP", I get both
'pupcamera' and 'pupmtp' popping up. If I choose MTP mode, just get
'pupmtp'. So I modified /usr/sbin/pupautodetect-run:
case $1 in
camera)
pupcamera &
;;
android-device) #20201205
sleep 2 #20201208 do not run pupmtp if running pupcamera
pidof pupcamera >/dev/null
if [ $? -ne 0 ];then
pupmtp &
fi
;;
esac
...select PTP on phone, a uevent occurs that
launches 'pupmtp', then another uevent launches 'pupcamera'. This code
prevents both from running, only 'pupcamera'. Select MTP on phone, only
'pupmtp' runs.
EDIT:
James (jamesbond in the forums) has responded, with another solution. James posted:
the "&" and "disown" probably won't work because the shell is not
connected to a controlling terminal, hence, it doesn't support job
control (which is required for "&" and "disown" to work).
Yes, that is the problem that I had in the
initrd, and the busybox solution to add job control in the initrd is the
'cttyhack' applet. And as posted above, 'cttyhack' also works for a
script called from a udev rule.
However, James has proposed another solution, that works with bash:
#!/bin/sh
set -m
... whatever stuff you want to do.
"set -m" turns on job control, as explained in bash man page:
https://linux.die.net/man/1/bash
"set -m" will probably also work with busybox ash.
Tags: easy