How to execute a shellscript when I plug-in a USB-device
Solution 1
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
...
Solution 2
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'"
Solution 3
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()
Peter Anthony
Updated on September 18, 2022Comments
-
Peter Anthony almost 2 years
I'm looking for a very simple form processing API for Java. Assuming the form input fields correspond to bean properties, and all beans have javax.Validation annotations, ideally the API would:
- Display a bean as an html form
- Populate the bean, including nested objects where applicable, using the request parameters
- Validate the input using Validation annotation
- If there is an error, display errors at top of form, and highlight error fields.
Additionally:
- It would be nice if i didn't have to buy into a whole application framework, since I am working with a legacy app.
- Allow configuration for more complicated use cases, but by default just use convention.
Bonus:
- generates javascript client side validation too.
Note: If this requires several different libraries, that is fine too.
Update:
Since I never found what I was looking for, and migrating to Spring was not an option, I went ahead and rolled my own solution. It is, affectionately, called java in jails (loosely modeled on rails form processing). It gives you dead simple (and pretty) form creation, client and server side validation, and request parameter to object mapping. No configuration required.
Example Bean:
public class AccountForm { @NotBlank(groups = RequiredChecks.class) @Size(min = 2, max = 25) private String name; //... }
Example Form:
<%@ taglib uri="http://org.jails.org/form/taglib" prefix="s" %> <s:form name="accountForm" action="/jails-demo/jails" label="Your Account Details" style="side"> <s:text name="name" label="Name" size="25" /> <s:text name="accountName" label="Account Name" size="15" /> ... </s:form>
Example Validation and Mapping:
SimpleValidator validator = new SimpleValidator(); if ("submit".equals(request.getParameter("submit"))) { Map<String, List<String>> errors = validator.validate(AccountForm.class, request.getParameterMap()); if (errors != null) { AccountForm account = validator.getMapper().toObject(AccountForm.class, request.getParameterMap()); //do something with valid account } else { SimpleForm.validateAs(AccountForm.class).inRequest(request).setErrors(errors); //handle error } } else { SimpleForm.validateAs(AccountForm.class).inRequest(request); //forward to formPage }
This is what the form looks like, with client side validation using jQuery (provided by Position Absolute):
-
CoolBeans almost 13 yearswe usually do not provide complete solutions like you have asked above. Why don't you start developing your "form" and then when you have more specific coding questions we can help you more?
-
Peter Anthony over 12 yearsThanks for the offer... I actually wasn't looking for coding help, I was looking for a n existing solution that I could just plug into.
-
Peter Anthony almost 13 yearsBeanUtils.populate(bean, request.getParameterMap())... that's half the battle. does it come with stock converters for String->Primitive->String or do yu have to write your own? unfortunately, writing the HTML was what the other thing I was most interested in trying to get around not having to do myself!
-
Peter Anthony almost 13 yearsscatch that. i see that there are stock converters for all the standard use cases, and you can create your own as needed. thanks again!
-
Redsandro over 11 yearsThis is interesting! It seems that it has no permission to write to /log/. It does write to /tmp/. I guess it had no permission to read my previous testscripts either.
-
Olaf Dietsche over 11 years@Redsandro This was not intentional, just for, well, testing purposes. Anyway, I'm glad it helped. ;-)
-
Redsandro about 11 yearsI would like to encourage you to also check out this question and see if your knowledge can be valuable there. :)
-
Avindra Goolcharan almost 8 yearsYou can also add
ACTION=="add",
directly to the rule definition. -
Gert van den Berg almost 6 yearsDoes not answer the question: This only covers boot-time (and using
udev
for other times are not a "few little modifications") and the Raspberry Pi. There is an unnecessarysudo
-rc.local
runs as root, it is a privilege escalation issue - a file that is editable by a normal user is run as root. -
jamescampbell over 5 yearsGreat use of named pipes here. I was wondering you could also just create an arbitrary file in tmp and look for it as well instead of a named pipe, correct?
-
Alexis Wilke over 3 years@jamescampbell The pipe allows you to do a
read
which blocks until something is written to the pipe. You use 0 CPU cycles during thatread
which is way better than checking for the existance of a file which would require a sleep (i.e. a polling solution) and the detection could happen seconds later if you use a long sleep. -
jamescampbell over 3 years@AlexisWilke thank you that is super useful to know why that pipe is better
-
Louis Kröger almost 3 yearsHuh. So
lsusb
providesidVendor:idProduct
in its output (per example rules file), but that same information is referred to asID_VENDOR_ID
andID_MODEL_ID
in the output ofudevadm info --query=all /device/path
. The naming inconsistency is confusing. Are model and product considered different info? -
Olaf Dietsche almost 3 years@sherrellbc I don't know. But consider, that
lsusb
is specific to USB, whileudevadm
is more general and covers a wider range of device types. -
Al F about 2 yearsWith disks automounting, code like
while [[ ! -e "$REMOTE_PATH" ]]; do sleep 1
seems to use 0 CPU as well. Since just waiting for the path works. are there still good reasons to use a pipe? -
Admin about 2 yearsthanks to your answer I was able to create solution that detects when I connect my USB keyboard and it changes the layout automatically - askubuntu.com/a/1414424/94200