Udev

Basics

Udev rules are created in

/etc/udev/rules.d/

Create a file named e.g. 10_my_udev.rules ... the leading number 10 should ensure that it runs before all other rules already on your system (unless you already have rules starting with 10 or smaller). The ending .rules is important for the file to be recognized as a udev rules file.

Specify the properties to be matched for the device you want to create a rule for and provide the action to be taken when it is (un)plugged. Basic example for a USB mouse: Run lsusb to get the vendor and product id:

root@host:~$ lsusb
 Bus 001 Device 010: ID 192f:0416 Avago Technologies, Pte.
 ... more irrelevant output

You could now specify the following rule:

 ATTRS{idVendor}=="192f", ATTRS{idProduct}=="0416", RUN+="/path/to/my/shellscript.sh"

Or, if e.g. you wanted to disable the touchpad upon plugin

 ACTION=="add", ATTRS{idVendor}=="192f", ATTRS{idProduct}=="0416", RUN+="/sbin/modprobe -r psmouse"

And to enable upon unplug

 ACTION=="remove", ATTRS{idVendor}=="192f", ATTRS{idProduct}=="0416", RUN+="/sbin/modprobe psmouse"

Symlinks

Device node names are typically not very intuitive. If you want to always have a device node called /dev/sdcard when an sdcard is inserted, use the SYMLINK directive:

ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="sd*1", ENV{ID_DRIVE_FLASH_SD}="1", SYMLINK="sdcard"

The KERNEL condition prevents multiple execution of the same rule.

Automatically open folders that contain certain files in a file-manager

E.g. always open folders with JPG files when an SCARD is inserted: Use the following rule:

ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="sd*1", ENV{ID_DRIVE_FLASH_SD}="1", RUN+="/usr/local/bin/handle_sd_card.sh"

In combination with the following script:

#!/bin/bash
# /usr/local/bin/handle_sd_card.sh
#
# Cleanup of old mounts
umount /tmp/sdcard.*
rmdir /tmp/sdcard.*
#
# Actual script
MOUNTPOINT=/tmp/sdcard.$BASHPID
FILE_EXTENSION=JPG
USER_FOR_FILE_BROWSER
if [ "${ACTION}" == "add" ];then
   mkdir -p $MOUNTPOINT 
   mount /dev/disk/by-uuid/${ID_FS_UUID} $MOUNTPOINT
   cd $MOUNTPOINT 
   export DISPLAY=:0
   for directory in $(find . -iname "*.$FILE_EXTENSION"| sed 's#.\*/.*#\1#' | sort -u) ; do
       cd $MOUNTPOINT/$directory
       su $USER_FOR_FILE_BROWSER -c thunar &  
   done
fi

Giving member of a certain group write access to usb device nodes

Let's say you want members of the group usbadmin to be able to write to usb device nodes. You can achieve it with a rule like this:

SUBSYSTEMS=="usb", ACTION=="add", GROUP="usbadmin"

Using properties in a shell script

You can also use the properties in a shell script you are calling. E.g. if you wanted to use only one rule, and let the shell script decide what to do based on the action, you could do

ATTRS{idVendor}=="192f", ATTRS{idProduct}=="0416", RUN+="/path/to/my/shellscript.sh"

in the rules file and

#!/bin/bash
if [ ${ACTION} == "add" ];then
  modprobe -r psmouse
elif [ ${ACTION} == "remove" ];then
  modprobe psmouse
fi

in the called shell script

More advanced matching rules

Get more detailed info on the device now:

lsusb -d 192f:0416 -v

Bus 001 Device 007: ID 192f:0416 Avago Technologies, Pte. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x192f Avago Technologies, Pte.
  idProduct          0x0416 
  bcdDevice            2.00
  iManufacturer           0 
  iProduct                2 USB Optical Mouse
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           34
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower               98mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      2 Mouse
      iInterface              0 
  HID Device Descriptor:
    bLength                 9
    bDescriptorType        33
    bcdHID               1.11
    bCountryCode            0 Not supported
    bNumDescriptors         1
    bDescriptorType        34 Report
    wDescriptorLength      71
   Report Descriptors: 
     ** UNAVAILABLE **
      Endpoint Descriptor:
  bLength                 7
  bDescriptorType         5
  bEndpointAddress     0x81  EP 1 IN
  bmAttributes            3
    Transfer Type            Interrupt
    Synch Type               None
    Usage Type               Data
  wMaxPacketSize     0x0006  1x 6 bytes
  bInterval              10
Device Status:     0x0000
  (Bus Powered)

Alternatively, call a script that outputs the environment generated by udev to a log file - this gives you a whole bunch of properties you can match against to specifiy the device.

You could now specify the following rule:

ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/path/to/my/shellscript.sh"

and /path/to/my/shellscript.sh:

#!/bin/bash
env >> /tmp/env.log

(Obviously make the script executable and find useful info in /tmp/env.log after the next hotplug. You can access the environment both in rules and in scripts.

E.g. for my mouse the output is

ID_USB_DRIVER=usbhid
ID_MODEL=USB_Optical_Mouse
ID_PATH_TAG=pci-0000_00_14_0-usb-0_4_1_0
ID_MODEL_ENC=USB\x20Optical\x20Mouse
ID_REVISION=0200
ID_BUS=usb
SUBSYSTEM=input
EV=17
ID_SERIAL=192f_USB_Optical_Mouse
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/input/input57
NAME="USB Optical Mouse"
ID_MODEL_ID=0416
ID_VENDOR_ENC=192f
ID_INPUT_MOUSE=1
ACTION=remove
MODALIAS=input:b0003v192Fp0416e0111-e0,1,2,4,k110,111,112,r0,1,6,8,am4,lsfw
PWD=/
UDEV_LOG=3
REL=143
KEY=70000 0 0 0 0
USEC_INITIALIZED=10222831102
ID_VENDOR_ID=192f
SHLVL=1
MSC=10
PHYS="usb-0000:00:14.0-4/input0"
ID_TYPE=hid
PRODUCT=3/192f/416/111
PROP=0
ID_VENDOR=192f
ID_USB_INTERFACE_NUM=00
ID_INPUT=1
UNIQ=""
SEQNUM=2437
ID_USB_INTERFACES=:030102:
ID_PATH=pci-0000:00:14.0-usb-0:4:1.0
_=/usr/bin/env
SUBSYSTEM=hidraw
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/0003:192F:0416.0027/hidraw/hidraw0
MINOR=0
ACTION=remove
PWD=/
UDEV_LOG=3
USEC_INITIALIZED=11145880798
MAJOR=251
DEVNAME=/dev/hidraw0
SHLVL=1
SEQNUM=2438
_=/usr/bin/env

To get a generic rule that does the touchpad dis/en-abling for any mouse you can now do

ACTION=="add", ATTRS{bInterfaceProtocol}=="02", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceSubClass}=="01", RUN+="/sbin/modprobe -r psmouse"
ACTION=="remove", ENV{ID_INPUT_MOUSE}=="?*", RUN+="/sbin/modprobe psmouse"

Gaining more info on the device using udevadm

Log at the syslog during the (un)plug event:

tail -f /var/log/syslog:
Nov 17 23:44:32 asus-n56v mtp-probe: checking bus 1, device 52: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-3"

Then run udevadm on the device path to get the devices properties:

udevadm info -a -p /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3

.. shows all attributes to match against. Note that plugin and unplug can point to different levels in the file system hierarchy - with folders representing parent devices of the files / folders in them. You can match against attributes of the device and one parent device.

Triggering Events

If e.g. at system startup the right states aren't picked up you can trigger them manually. E.g. for a USB mouse to be more reliably detected, you could run the following (as root) upon session start:

/sbin/udevadm trigger --action="add" --subsystem-match="usb"

Finding good examples

On Debian, the good stuff can be found in /lib/udev/rules.d/