Last weekwe learned the basics of the udev filesystem, and how to dig up device names. Today we dive into writing custom udev rules. Why would you want to acquire this strange knowledge? Because, believe it or not, computers are not yet perfect, so sometimes we must fix them. USB scanners are notorious examples of needing human intervention to be usable by non-root users. Managing device permissions is something you’re bound to bump into sooner or later – the traditional Unix method of user and group permissions no longer works on udev-managed devices. USB devices are moving targets that take on different names if you don’t nail them down. If you want to give them friendly, memorable names, you’ll need to know how to configure udev.
Getting Scanners To Work Right
Scanners and multi-function printer/copier/scanner/faxes are notorious for not being automatically usable on Linux. So let’s take a stroll down Making Them Work Lane. In these examples we’ll use the Epson Stylus CX4800. (Not because it is a great machine, but because it’s what I happen to have. It runs through ink at astonishing speeds when I can get it to print at all, because it uses special inks that clot like blood and clog the print nozzles. Then it cleans the nozzles with ink. Brilliant, eh?)
First of all, is your scanner/multi-function thingy even supported in Linux? If it isn’t, even udev does not contain enough magic to make it work. Check out Sane-project.org and Google before you buy one.
Take a peek in your /etc/udev/rules.d/*-libsane-rules file. Naturally, the different distributions have to scatter all over the udev landscape like a herd of cats and do things their in their own weird ways. On Kubuntu, it’s 45-libsane.rules. On Fedora, 60-libsane.rules. The number is the priority- lower numbered-files are parsed first. This is an example of a stock *-libsane-rules file:
SUBSYSTEM!="usb_device", ACTION!="add", GOTO="libsane_rules_end"# Hewlett-Packard|ScanJet 4100C SYSFS{idVendor}=="03f0", SYSFS{idProduct}=="0101", MODE="664", GROUP="scanner" # Hewlett-Packard|PhotoSmart S20 SYSFS{idVendor}=="03f0", SYSFS{idProduct}=="0102", MODE="664", GROUP="scanner" # Hewlett-Packard|ScanJet 4200C '...'
It seems rather ironic that udev makes a big deal about slimming down /dev, then stuffs files like this onto your system with all manner of irrelevant entries. Oh well, I’m just an old country sysadmin, so what do I know? Anyway it’s easy enough to delete the irrelevant entries.
Make sure you have SANE installed, including the documentation, backends and utilities. Have your scanner powered up and connected to your computer. Then run this command:
# sane-find-scanner # sane-find-scanner will now attempt to detect your scanner '...' found USB scanner (vendor=0x04b8, product=0x0819) at libusb:003:002 # Your USB scanner was (probably) detected. It may or may not be supported by # SANE. Try scanimage -L and read the backend's manpage '...'
Now try scanimage -L as an ordinary user:
$ scanimage -L
If it doesn’t find your scanner, run it as root:
# scanimage -L device `epson:libusb:003:002' is a Epson Unknown model flatbed scanner
This tells you there is problem with the scanner permissions. No problem, we can fix that. First edit /etc/sane.d/epson.conf to configure the backend correctly. Comment out all lines except these two:
usb usb 0x4b8 0x0819
Where did those nice hexadecimal numbers come from? From running the sane-find-scanner command. Make sure you get the numbers in the correct order. Next, add these lines to /etc/udev/rules.d/*-libsane-rules:
#epson stylus cx4800 SYSFS{idVendor}=="04b8", SYSFS{idProduct}=="0819", MODE="664", GROUP="scanner"
Pretty much just copy-and-paste, following the examples of the other rules in the file. Note that you can drop leading zeroes.
In part 1 we learned how to search /sys for unique IDs to use in our rules. This is what the Epson looks like, with the uninteresting bits snipped:
$ udevinfo -a -p /class/usb_device/usbdev3.2 follow the "device"-link to the physical device: looking at the device chain at '/sys/devices/pci0000:00/0000:00:02.2/usb3/3-6': BUS=="usb" ID=="3-6" DRIVER=="usb" SYSFS{configuration}=="USB2.0 MFP_Hi-Speed_" SYSFS{devnum}=="2" SYSFS{idProduct}=="0819" SYSFS{idVendor}=="04b8" SYSFS{manufacturer}=="EPSON" SYSFS{product}=="USB2.0 MFP_Hi-Speed_" SYSFS{serial}=="L84010507120939390" SYSFS{speed}=="480" SYSFS{version}==" 2.00"
It’s all there when you know where to look. Save your changes and reboot, and voilá! No more do you need to run as root to use your scanner.
SANE comes with a bale of man pages which are full of interesting deep scanner voodoo. man sanelists all of them.
Permissions and Ownership, Names, and Symlinks
udev configurations fall into three general categories: permissions and ownership, names, and symlinks. Kubuntu organizes these neatly into three different files: 20-names.rules, 40-permissions.rules, and 60-symlinks.rules. In Fedora they’re not separated by those categories, as you can see by studying 50-udev.rules, which has all three. You may edit the existing files, or put all of your modifications in a separate file like 10-local.rules.
If you need to change device permissions or ownership, you’ll need to troll through /etc/udev/rules.d to make sure you find all the pertinent rules. A quick way to find things is with the grep command, like this example that looks for devices assigned to the disk group:
$ grep -ir 'group="disk"' /etc/udev /etc/udev/rules.d/40-permissions.rules:SYSFS{removable}!="1", GROUP="disk" /etc/udev/rules.d/40-permissions.rules:KERNEL=="raw1394", GROUP="disk"
It even tells you the filename. You may also view the previous five lines with the -B 5 option, and the following five lines with the -A 5, or whatever number of lines you want to get the whole context.
Creating Persistent Device Names
Linux has come a long way in a few short years. Modern distributions handle a multitude of hardware devices, and can hotplug just about everything. The downside is non-persistent device names. Your Linux distribution should come with a batch of pre-fab persistent names for hard drives and CD/DVD drives. But devices like USB storage drives or USB network cards might still be moving targets.
There are two ways to give additional names to devices. One is to add another name, another way is to create symlinks. For example, my second hard drive, /dev/sda, is a mondo 300-gigabyte SATA drive that I use for storing photo archives. I can create a 10-local.rules file with this entry:
# nickname for /dev/sda KERNEL=="sda", NAME="photo-archives"
After reboot there will be a /dev/photo-archives. I can use this like any storage drive- make an entry in /etc/fstab using the /dev/photo-archives name, mount it manually, whatever I like.
While this is fast and easy, it’s better to create a symlink, because symlinks point back to the original device node. Names do not, so I’ll have to search /etc/udev to find out what it really is, because I will forget. Run udevinfo to see the drive information:
$ udevinfo -a -p /block/sda '...' device '/sys/block/sda' has major:minor 8:0 looking at class device '/sys/block/sda': KERNEL=="sda" SUBSYSTEM=="block" SYSFS{dev}=="8:0" SYSFS{range}=="16" SYSFS{removable}=="0" SYSFS{size}=="156301488" '...'
Then choose an attribute or two and create a symlink like this:
# nickname for /dev/sda KERNEL=="sda", SYSFS{size}=="156301488", SYMLINK+="photo-archives"
If you use more than one sysfs attribute, make sure they come from the same section of the udevinfo output, or it won’t work. Mind your punctuation- the KERNEL and SYSFS values are called match keys, and must use the equality operator or ==. SYMLINK is an assignment key. The plus sign means “add this symlink to the list of symlinks”, and = means “assign this value”. If you leave off the plus sign, you’ll overwrite the existing symlinks.
I added the size for insurance, as it’s the only drive of that size on the system. If either of the match keys change the rule will not work.
Reboot and, hurrah, the new symlink shows up just like it’s supposed to:
$ ls -l /dev/photo-archives lrwxrwxrwx 1 root root 3 2006-10-09 22:19 /dev/photo-archives -> sda
Nailing Down USB Network Interfaces
Ordinary PCI Ethernet cards are best given permanent name assignments by using ifrename on Debian systems, and via the usual configuration methods on Fedora. (See Nail Down Network Interface Names with ifrename.) USB NICs are another kettle of clams entirely, but a simple one. All you need is the MAC address and then a rule like this example for a wireless USB NIC:
KERNEL=="wlan*", SYSFS{address}=="00.11.22.33.44.55", NAME="wlan0"
You don’t have to use wlan0; you can call it anything you like- fred, barney, wan-thing, whatever you want. Just remember to use the name in all of your configuration and startup files.
You may use /sbin/ifconfig -a to obtain MAC addresses and kernel names, or udevinfo:
$ udevinfo -a -p /sys/class/net/wlan0
All network interfaces are in /sys/class/net/.
Reloading Udev Changes
On Kubuntu you must reboot to activate changes, as it uses a very different udev implementation with a customized command set. (Translation: lots of missing bits, but lovely rules.d files.) On Fedora and its relatives, reload rule changes and restart udev with these commands:
# udevcontrol reload_rules # udevstart
This is all rather a lot of hassle, but keep in mind that udev is still a youngun, so sometime down the road we’re bound to see some nicer tools for customizing udev rules. Especially if some inspired user helps to create them.
Resources
- man udev
- Writing udev rules. This howto by Daniel Drake is the ultimate resource for learning how to write udev rules.