How to execute a shellscript when I plug-in a USB-device
If you want to run the script on a specific device, you can use the vendor and product ids
In
/etc/udev/rules.d/test.rules
:ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
in
test.sh
:#! /bin/sh env >>/tmp/test.log file "/sys${DEVPATH}" >>/tmp/test.log if [ "${ACTION}" = add -a -d "/sys${DEVPATH}" ]; then echo "add ${DEVPATH}" >>/tmp/test.log fi
With env
, you can see what environment is set from udev and with file
, you will discover the file type.
The concrete attributes for your device can be discovered with lsusb
lsusb
gives
...
Bus 001 Device 016: ID 152d:2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge
...
This isn't directly about your question but about what you're doing. If you start a backup script from udev you will face two main issues :
- Your script might be started before the device is ready and can be mounted, you have to keep the KERNEL=="sd*" condition if you want to use the /dev node to mount it
- More important, if your script takes some time to execute (which can easily be the case with a backup script) it will be killed shortly after it is started (about 5s)
- You will face many complicated user permission issues
My advice is to create a script in your user home which listens to a named pipe and which will be started asynchronously like :
#!/bin/bash
PIPE="/tmp/IomegaUsbPipe"
REMOTE_PATH="/path/to/mount/point"
LOCAL_PATH="/local/path/"
doSynchronization()
{
#your backup here
}
trap "rm -f $PIPE" EXIT
#If the pipe doesn't exist, create it
if [[ ! -p $PIPE ]]; then
mkfifo $PIPE
fi
#If the disk is already plugged on startup, do a syn
if [[ -e "$REMOTE_PATH" ]]
then
doSynchronization
fi
#Make the permanent loop to watch the usb connection
while true
do
if read line <$PIPE; then
#Test the message read from the fifo
if [[ "$line" == "connected" ]]
then
#The usb has been plugged, wait for disk to be mounted by KDE
while [[ ! -e "$REMOTE_PATH" ]]
do
sleep 1
done
doSynchronization
else
echo "Unhandled message from fifo : [$line]"
fi
fi
done
echo "Reader exiting"
Note : I use auto-mount with kde so I check for the folder to appear. You can pass the /dev/sd* parameter in the fifo from the udev rule and mount it yourself in the script. To write to the fifo don't forget that udev is not a shell and that redirection doesn't work. Your RUN should be like :
RUN+="/bin/sh -c '/bin/echo connected >> /tmp/IomegaUsbPipe'"
I've posted a solution on https://askubuntu.com/a/516336 and I'm also copy-pasting the solution over here.
I wrote a Python script using pyudev that I leave running in the background. That script listens to udev events (thus, it's very efficient) and runs whatever code I want. In my case, it runs xinput
commands to setup my devices (link to the most recent version).
Here's a short version of the same script:
#!/usr/bin/env python3
import pyudev
import subprocess
def main():
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='usb')
monitor.start()
for device in iter(monitor.poll, None):
# I can add more logic here, to run different scripts for different devices.
subprocess.call(['/home/foo/foobar.sh', '--foo', '--bar'])
if __name__ == '__main__':
main()