Access USB device with Bash script

I am using a Raspberry Pi 4 Model B. I am trying to make a script that runs automatically whenever a USB device is plugged in. It will try to read from a text file on the USB device.

This is what I have achieved so far. The USB triggers a udev event that runs my script. The script has root privileges. When it gets executed it will run ls /media/username after any mounted devices.

Here is where I run into some problems. The script (when executed by the udev event) won’t find my USB device. But the script works when I execute it manually. So the script isn’t failing (I am using absolute paths, and no environment variables) all of the commands works when I type them in the terminal and so on. I think it’s a permission issue but I could be wrong.

Asked By: XII


Instead of disabling the private mount namespace (which was added to udev to close up a security hole), let me present an alternative solution:

First, make an udev rule that matches your device and adds a TAG+="systemd" to it. This will tell udev to make the device known to systemd as a *.device unit.

By default, the name of the unit will match the sysfs path of the device, with slashes replaced by dashes, i.e. if ttyS0 appears in /sys as /sys/devices/pnp0/00:01/tty/ttyS0, the corresponding device unit would appear as sys-devices-pnp0-00:01-tty-ttyS0.device. If that name is inconvenient to you, add a ENV{SYSTEMD_ALIAS}+="<desired device unit name>" to create a second name you can decide.

The aliases will still go through the slashes-to-dashes process, so if you want something like sys-subsystem-media-devices-somename.device (similar to what other subsystems already do), you would specify ENV{SYSTEMD_ALIAS}+="/sys/subsystem/media/devices/somename".

Once the device appears as a suitably-named *.device unit in systemd, you can have your custom script as a service that specifies WantedBy="sys-subsystem-media-devices-somename.device", causing that systemd unit to be started when the device appears. If your script does not need root access and should to run only when there is a logged-in user, you can even make it a user service, i.e. defined in /etc/systemd/user/*.service for all users, or in ~/.config/systemd/user/*.service for a particular user only.

Alternatively, you can provide the service dependency within the udev rules too, by specifying one of these in your udev rule:

  • ENV{SYSTEMD_WANTS}="<system service to start on hotplug>.service"
  • ENV{SYSTEMD_USER_WANTS}="<user service to start on hotplug>.service"

This gives you multiple ways to distribute responsibilities between the udev rule and the service, depending on how modular your software is and how optional to the user(s) you want it to be:

  • If you specify it as a dependency on a system service, the service will be triggered whether any user is logged in or not, and you’ll need to mask or uninstall the service (with root privileges) if you want to disable it.
  • If you specify it as a udev-specified dependency on a user service, the user can choose whether to allow the service to run or not without needing root permissions to change it for themselves; together with a default user service for all users, this would provide an opt-out mechanism for individual users.
  • If you just specify the udev options for the device unit creation in the udev rule, it allows the service(s) to opt-in for autostarting when the device appears. This might be useful if you want the service part to be independently updateable.
Answered By: telcoM
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.