Making udev rule to set usb device permissions
I have a USB temperature probe that defaults to 600 permission and ownership by root. I'd like everyone (especially my non-root script) to be able to read this device, so I set it to 644 permission. But something keeps switching the permission back. I assume this something is udev. To make my permission setting stick, I told udev to honor it by creating this file:
root@weasel:/etc/udev/rules.d# pwd;ls /etc/udev/rules.d 45-libmtp7.rules 70-persistent-net.rules README 70-persistent-cd.rules 99-vernier.rules root@weasel:/etc/udev/rules.d# cat 99-vernier.rules # Vernier EasyTemp/Go!Temp ATTR{idVendor}=="08f7", ATTR{idProduct}=="0002", MODE="644"
I guessed at the syntax by peeking at the other rules files in this directory.
My previous attempt at a fix for this was to add a "chmod" to /etc/rc.local, but udev is smarter than that, since it changes the permission of my usb device at times other than reboot. We'll see if this udev rule is sufficient. If not, I'll have to start reading more about udev.
I disabled the /etc/rc.local script and rebooted... immediately after booting, the permissions on /dev/ldusb0 were 600 again.
Bummer.
udevadm query[edit]
OK, getting a little more serious about understanding udev, I am starting with this document: http://reactivated.net/writing_udev_rules.html
And I also learn that the udevinfo command doesn't exist in Ubuntu, but there does exist a udevadm command and it works like this:
root@weasel:/sys/dev/char# udevadm info -q all -n /dev/ldusb0 P: /devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0 N: ldusb0 S: char/180:176 E: UDEV_LOG=3 E: DEVPATH=/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0 E: SUBSYSTEM=usb E: DEVNAME=/dev/ldusb0 E: MAJOR=180 E: MINOR=176 E: DEVLINKS=/dev/char/180:176
That's cool, but where is the corresponding program "udevtest"? Ahh it is also in udevadm:
root@weasel:/sys/dev/char# udevadm test /sys/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0 run_command: calling: test udevadm_test: version 163 This program is for debugging only, it does not run any program, specified by a RUN key. It may show incorrect results, because some values may be different, or not available at a simulation run. parse_file: reading '/lib/udev/rules.d/40-fuse-utils.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-gnupg.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-hplip.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-ia64.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-kino.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-libgphoto2-2.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-libpisock9.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-libsane.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-ppc.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-usb-media-players.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-usb_modeswitch.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-virtualbox-ose-dkms.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-xserver-xorg-video-intel.rules' as rules file parse_file: reading '/lib/udev/rules.d/41-mythtv-permissions.rules' as rules file parse_file: reading '/lib/udev/rules.d/45-fuse.rules' as rules file parse_file: reading '/etc/udev/rules.d/45-libmtp7.rules' as rules file parse_file: reading '/lib/udev/rules.d/45-libmtp8.rules' as rules file parse_file: reading '/lib/udev/rules.d/50-firmware.rules' as rules file parse_file: reading '/lib/udev/rules.d/50-udev-default.rules' as rules file parse_file: reading '/lib/udev/rules.d/55-dm.rules' as rules file parse_file: reading '/lib/udev/rules.d/56-hpmud_support.rules' as rules file parse_file: reading '/lib/udev/rules.d/56-lvm.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-cdrom_id.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-floppy.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-alsa.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-input.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-serial.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage-dm.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage-lvm.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage-tape.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-v4l.rules' as rules file parse_file: reading '/lib/udev/rules.d/61-gnome-bluetooth-rfkill.rules' as rules file parse_file: reading '/lib/udev/rules.d/61-mobile-action.rules' as rules file parse_file: reading '/lib/udev/rules.d/61-persistent-storage-edd.rules' as rules file parse_file: reading '/lib/udev/rules.d/64-xorg-xkb.rules' as rules file parse_file: reading '/lib/udev/rules.d/66-xorg-synaptics.rules' as rules file parse_file: reading '/lib/udev/rules.d/69-xorg-vmmouse.rules' as rules file parse_file: reading '/lib/udev/rules.d/69-xserver-xorg-input-wacom.rules' as rules file parse_file: reading '/lib/udev/rules.d/70-acl.rules' as rules file parse_file: reading '/lib/udev/rules.d/70-hid2hci.rules' as rules file parse_file: reading '/etc/udev/rules.d/70-persistent-cd.rules' as rules file parse_file: reading '/etc/udev/rules.d/70-persistent-net.rules' as rules file parse_file: reading '/lib/udev/rules.d/70-printers.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-cd-aliases-generator.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-net-description.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-persistent-net-generator.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-probe_mtd.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-tty-description.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-ericsson-mbm.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-longcheer-port-types.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-pcmcia-device-blacklist.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-platform-serial-whitelist.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-simtech-port-types.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-usb-device-blacklist.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-zte-port-types.rules' as rules file parse_file: reading '/lib/udev/rules.d/78-graphics-card.rules' as rules file parse_file: reading '/lib/udev/rules.d/78-sound-card.rules' as rules file parse_file: reading '/lib/udev/rules.d/79-fstab_import.rules' as rules file parse_file: reading '/lib/udev/rules.d/80-alsa.rules' as rules file parse_file: reading '/lib/udev/rules.d/80-drivers.rules' as rules file parse_file: reading '/lib/udev/rules.d/80-udisks.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-brltty.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-console-setup.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-hdparm.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-hplj10xx.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-lvm2.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-pcmcia.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-regulatory.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-usbmuxd.rules' as rules file parse_file: reading '/lib/udev/rules.d/90-hal.rules' as rules file parse_file: reading '/lib/udev/rules.d/90-libgpod.rules' as rules file parse_file: reading '/lib/udev/rules.d/90-pulseaudio.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-keyboard-force-release.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-keymap.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-udev-late.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-dell.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-fujitsu.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-gateway.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-ibm.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-lenovo.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-toshiba.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-csr.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-hid.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-wup.rules' as rules file parse_file: reading '/lib/udev/rules.d/97-bluetooth.rules' as rules file parse_file: reading '/etc/udev/rules.d/99-vernier.rules' as rules file parse_file: reading '/dev/.udev/rules.d/root.rules' as rules file udev_rules_new: rules use 252984 bytes tokens (21082 * 12 bytes), 36753 bytes buffer udev_rules_new: temporary index used 61220 bytes (3061 * 20 bytes) udev_device_new_from_syspath: device 0x7fb328725d50 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0' udev_device_new_from_syspath: device 0x7fb3287161b0 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0' udev_device_read_db: device 0x7fb3287161b0 filled with db symlink data '/dev/ldusb0' udev_device_new_from_syspath: device 0x7fb32872eb80 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0' udev_device_new_from_syspath: device 0x7fb32872ee70 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8' udev_device_new_from_syspath: device 0x7fb32872f180 has devpath '/devices/pci0000:00/0000:00:02.0/usb2' udev_device_new_from_syspath: device 0x7fb32872f480 has devpath '/devices/pci0000:00/0000:00:02.0' udev_device_new_from_syspath: device 0x7fb32872f750 has devpath '/devices/pci0000:00' udev_rules_apply_to_event: LINK 'char/180:176' /lib/udev/rules.d/50-udev-default.rules:4 udev_rules_apply_to_event: RUN 'socket:@/org/freedesktop/hal/udev_event' /lib/udev/rules.d/90-hal.rules:2 udev_event_execute_rules: no node name set, will use kernel supplied name 'ldusb0' udev_device_update_db: created db link (ldusb0 char/180:176) udev_node_add: creating device node '/dev/ldusb0', devnum=180:176, mode=0600, uid=0, gid=0 udev_node_mknod: preserve file '/dev/ldusb0', because it has correct dev_t udev_node_mknod: set permissions /dev/ldusb0, 020600, uid=0, gid=0 node_symlink: preserve already existing symlink '/dev/char/180:176' to '../ldusb0' udevadm_test: UDEV_LOG=6 udevadm_test: DEVPATH=/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0 udevadm_test: MAJOR=180 udevadm_test: MINOR=176 udevadm_test: DEVNAME=/dev/ldusb0 udevadm_test: ACTION=add udevadm_test: SUBSYSTEM=usb udevadm_test: DEVLINKS=/dev/char/180:176 udevadm_test: run: 'socket:@/org/freedesktop/hal/udev_event'
Hey, that shows me the culprit who keeps switching the permissions on this file to 600. Curse you, udev_node_mknod!
So I just need to create a rule that will run after udev_node_mknod and undo its mischief.
I also see that my rule file was parsed, but never triggered. Evidently because it did not match. It looks like only two rules matched: 50-udev-default and 90-hal.
making a rule[edit]
Rules are pretty simple: a series of matching pairs and an action pair. Matching pairs are coupled by the equivalence operator (==) and action pairs are coupled by the assignment operator (=). All matching pairs must be true for the rule to trigger.
I'm pretty sure that I want my action pair to be this:
MODE="644"
So I just have to figure out the right way to uniquely describe my device. My guess about idVendor and idProduct were taken from the lsusb command and that didn't work. The matching is done on the /sys filesystem tree, so I have to find a property in there to match on. The udevinfo output contains this likely suspect:
DEVNAME=/dev/ldusb0
The syntax of the HOWTO I'm reading is wildly different from what udevadm is telling me. So I'll base my syntax on the local udev manpage.
The most direct rule looks like the one matches the device path (/dev/ldusb0). I can use this because I'm trying to make a change late in the process, after the device has been established. So my rule might be this:
DEVPATH=="/dev/ldusb0", MODE="644"
So I put that in my rule file and run udevadm test again.
OK, that didn't work either, but it turns out this simple rule does work:
KERNEL=="ldusb0", MODE="644"
root@weasel:/etc/udev/rules.d# udevadm test /sys/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0 run_command: calling: test udevadm_test: version 163 This program is for debugging only, it does not run any program, specified by a RUN key. It may show incorrect results, because some values may be different, or not available at a simulation run. parse_file: reading '/lib/udev/rules.d/40-fuse-utils.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-gnupg.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-hplip.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-ia64.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-kino.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-libgphoto2-2.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-libpisock9.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-libsane.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-ppc.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-usb-media-players.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-usb_modeswitch.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-virtualbox-ose-dkms.rules' as rules file parse_file: reading '/lib/udev/rules.d/40-xserver-xorg-video-intel.rules' as rules file parse_file: reading '/lib/udev/rules.d/41-mythtv-permissions.rules' as rules file parse_file: reading '/lib/udev/rules.d/45-fuse.rules' as rules file parse_file: reading '/etc/udev/rules.d/45-libmtp7.rules' as rules file parse_file: reading '/lib/udev/rules.d/45-libmtp8.rules' as rules file parse_file: reading '/lib/udev/rules.d/50-firmware.rules' as rules file parse_file: reading '/lib/udev/rules.d/50-udev-default.rules' as rules file parse_file: reading '/lib/udev/rules.d/55-dm.rules' as rules file parse_file: reading '/lib/udev/rules.d/56-hpmud_support.rules' as rules file parse_file: reading '/lib/udev/rules.d/56-lvm.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-cdrom_id.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-floppy.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-alsa.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-input.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-serial.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage-dm.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage-lvm.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage-tape.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-storage.rules' as rules file parse_file: reading '/lib/udev/rules.d/60-persistent-v4l.rules' as rules file parse_file: reading '/lib/udev/rules.d/61-gnome-bluetooth-rfkill.rules' as rules file parse_file: reading '/lib/udev/rules.d/61-mobile-action.rules' as rules file parse_file: reading '/lib/udev/rules.d/61-persistent-storage-edd.rules' as rules file parse_file: reading '/lib/udev/rules.d/64-xorg-xkb.rules' as rules file parse_file: reading '/lib/udev/rules.d/66-xorg-synaptics.rules' as rules file parse_file: reading '/lib/udev/rules.d/69-xorg-vmmouse.rules' as rules file parse_file: reading '/lib/udev/rules.d/69-xserver-xorg-input-wacom.rules' as rules file parse_file: reading '/lib/udev/rules.d/70-acl.rules' as rules file parse_file: reading '/lib/udev/rules.d/70-hid2hci.rules' as rules file parse_file: reading '/etc/udev/rules.d/70-persistent-cd.rules' as rules file parse_file: reading '/etc/udev/rules.d/70-persistent-net.rules' as rules file parse_file: reading '/lib/udev/rules.d/70-printers.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-cd-aliases-generator.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-net-description.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-persistent-net-generator.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-probe_mtd.rules' as rules file parse_file: reading '/lib/udev/rules.d/75-tty-description.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-ericsson-mbm.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-longcheer-port-types.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-pcmcia-device-blacklist.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-platform-serial-whitelist.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-simtech-port-types.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-usb-device-blacklist.rules' as rules file parse_file: reading '/lib/udev/rules.d/77-mm-zte-port-types.rules' as rules file parse_file: reading '/lib/udev/rules.d/78-graphics-card.rules' as rules file parse_file: reading '/lib/udev/rules.d/78-sound-card.rules' as rules file parse_file: reading '/lib/udev/rules.d/79-fstab_import.rules' as rules file parse_file: reading '/lib/udev/rules.d/80-alsa.rules' as rules file parse_file: reading '/lib/udev/rules.d/80-drivers.rules' as rules file parse_file: reading '/lib/udev/rules.d/80-udisks.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-brltty.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-console-setup.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-hdparm.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-hplj10xx.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-lvm2.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-pcmcia.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-regulatory.rules' as rules file parse_file: reading '/lib/udev/rules.d/85-usbmuxd.rules' as rules file parse_file: reading '/lib/udev/rules.d/90-hal.rules' as rules file parse_file: reading '/lib/udev/rules.d/90-libgpod.rules' as rules file parse_file: reading '/lib/udev/rules.d/90-pulseaudio.rules' as rules file parse_file: reading '/etc/udev/rules.d/90-vernier.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-keyboard-force-release.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-keymap.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-udev-late.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-dell.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-fujitsu.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-gateway.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-ibm.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-lenovo.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-toshiba.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-csr.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-hid.rules' as rules file parse_file: reading '/lib/udev/rules.d/95-upower-wup.rules' as rules file parse_file: reading '/lib/udev/rules.d/97-bluetooth.rules' as rules file parse_file: reading '/dev/.udev/rules.d/root.rules' as rules file udev_rules_new: rules use 252972 bytes tokens (21081 * 12 bytes), 36755 bytes buffer udev_rules_new: temporary index used 61220 bytes (3061 * 20 bytes) udev_device_new_from_syspath: device 0x7f0eddcffd50 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0' udev_device_new_from_syspath: device 0x7f0eddcf01b0 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0' udev_device_read_db: device 0x7f0eddcf01b0 filled with db symlink data '/dev/ldusb0' udev_device_new_from_syspath: device 0x7f0eddd08b80 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0' udev_device_new_from_syspath: device 0x7f0eddd08e70 has devpath '/devices/pci0000:00/0000:00:02.0/usb2/2-8' udev_device_new_from_syspath: device 0x7f0eddd09180 has devpath '/devices/pci0000:00/0000:00:02.0/usb2' udev_device_new_from_syspath: device 0x7f0eddd09480 has devpath '/devices/pci0000:00/0000:00:02.0' udev_device_new_from_syspath: device 0x7f0eddd09750 has devpath '/devices/pci0000:00' udev_rules_apply_to_event: LINK 'char/180:176' /lib/udev/rules.d/50-udev-default.rules:4 udev_rules_apply_to_event: RUN 'socket:@/org/freedesktop/hal/udev_event' /lib/udev/rules.d/90-hal.rules:2 udev_rules_apply_to_event: MODE 0644 /etc/udev/rules.d/90-vernier.rules:4 udev_event_execute_rules: no node name set, will use kernel supplied name 'ldusb0' udev_device_update_db: created db link (ldusb0 char/180:176) udev_node_add: creating device node '/dev/ldusb0', devnum=180:176, mode=0644, uid=0, gid=0 udev_node_mknod: preserve file '/dev/ldusb0', because it has correct dev_t udev_node_mknod: set permissions /dev/ldusb0, 020644, uid=0, gid=0 node_symlink: preserve already existing symlink '/dev/char/180:176' to '../ldusb0' udevadm_test: UDEV_LOG=6 udevadm_test: DEVPATH=/devices/pci0000:00/0000:00:02.0/usb2/2-8/2-8:1.0/usb/ldusb0 udevadm_test: MAJOR=180 udevadm_test: MINOR=176 udevadm_test: DEVNAME=/dev/ldusb0 udevadm_test: ACTION=add udevadm_test: SUBSYSTEM=usb udevadm_test: DEVLINKS=/dev/char/180:176 udevadm_test: run: 'socket:@/org/freedesktop/hal/udev_event'
I had reasonable confidence that everything would work just fine, thanks to the transparency of the udev test command. And after the next server reboot, it was confirmed that the ldusb0 device was given the proper permissions.
drobo[edit]
Now I need to build a rule to help my drobo register properly after a reboot. It used to work just fine, but recently it began to go dark after a reboot. I thought maybe it was a timing issue caused by adding more hard disks. But even if I give it all the time in the world, it still never spins up. The workaround is to power cycle it, or to unplug/replug the firewire cable, and then manual mount the disks.
Now that I have this udev knowledge, I should be able to fix this problem as well.