On Wed, Aug 25, 2021 at 2:26 PM Manuel Wagesreither <man...@fastmail.fm> wrote: > > Hello all, > > this is my first post on this mailing list and, first of all, I'd like to > thank you and appreciate your work on systemd in general. I admire the logic, > the completeness of the manpages and in general how beautifully things are > engineered. I'm no unix graybeard and systemd saved me from having to learn > all that legacy stuff systemd replaces. Compared to fstab, > /etc/network/interfaces and init.d, systemd is a piece of art. > > --- > > I'm working on an embedded device which should access and scan connected usb > drives for certain files. I seem to witness a race condition with my current > solution. I would ask for advice on how to implement this functionality in a > better way. > > When a device /dev/sdb1 is connected, the udev rule below starts BOTH > * a systemd-service "start-standalone-mender-deployment@media-sdb1.service" > * `systemd-mount --no-block --automount=no --options=ro --collect /dev/sdb1 > /media/sdb1` > > The service then starts a shell script accessing the usb drive. Occasionally, > it says the directory the usb drive is mounted at is empty. When checking > manually, I see it's not. I strongly suspect the script accessed the > directory before the usb drive got mounted there. > > Here's the udev rule: > ``` > ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", KERNEL=="*[0-9]*", > ENV{ID_FS_USAGE}=="filesystem", TAG+="systemd", > ENV{SYSTEMD_WANTS}+="start-standalone-mender-deployment@media-$name.service", > RUN{program}+="/usr/bin/systemd-mount --no-block --automount=no --options=ro > --collect $devnode /media/$name" > ``` > > And here's the systemd service: > It is templated and gets instantiated with "media-sdb1". It therefore has an > "After=media-sdb1.mount". I suspect Systemd-udevd executes the > ENV{SYSTEMD_WANTS} part before the RUN{program} part. Hence, > "media-sdb1.mount" doesn't yet exist when the service gets started, as it > gets created a tad later by systemd-mount. > > ``` > [Unit] > Description=Start standalone Mender deployment (%i) > After=%i.mount > > [Service] > Type=oneshot > Restart=no > ExecStart=/bin/sh /usr/bin/start-standalone-mender-deployment.sh /%I > ``` > > Can you confirm my theory? >
This sounds reasonable. Yet again the same confusion - dependencies are between jobs. "After=%i.mount" actually means "wait until start job for %i.mount completes before selecting start job for your unit". If the start job for your unit happens to be selected for execution before the start job for %i.mount was queued (it is quite possible as systemd-mount invocation takes time), there is no start job to wait for and After becomes noop. > The only alternative I see is to invoke systemd-mount without --no-block from > the shell script itself. Instead of communicating the mount point > (media-sdb1) via unit template parameter, I would communicate the device path > (/dev/sdb1) to the template unit and pass it on to the shell script, which > would determine mount point based on that. > Yes, there is no nice solution. Systemd was not designed for dynamic dependencies like in your case. While it is possible to add Wants=%i.mount to your unit, there is no guarantee that this unit definition will be present (due to the same race condition). Sometimes in this case a unit definition file is created and "systemctl daemon-reload" is invoked. This has its own can of worms so can hardly be recommended. So your approach is probably the most practical one. Hmm ... if systemd-mount --property accepts Wants and Before, your mount unit could pull in your service unit. I cannot test right now. > I'm all ears if you have comments or advice on that. I guess I'm not the > first one implementing something like this. > > > Regards, > Manuel > > P.S.: I won't be able to respond until Sunday Aug 29th.