Page 1 of 1

Using udev to Manage Hardware in Linux A Howto on Writing udev rules for Kernel 2.6.x Rate Topic: ***** 2 Votes

#1 rockstar_  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 33
  • View blog
  • Posts: 189
  • Joined: 16-October 06

Posted 23 October 2006 - 04:14 PM

Using udev to manage hardware

Why?
The first question is, why would you write a tutorial on using udev? Aren't there enough out there already?! I'd say yes, but in order for me to REALLY start getting the idea and writing my own rules, it required me to read almost all of them, and piece together the good parts of each one. I thought I could bring together what I had going on, and then possibly share what I had figured out.
The second question is, why would I need to learn to write custom udev rules? Well, take, for instance, my standard desktop system. I've got a card reader that reads 5,195,638,923,579,823 different types of cards (that's a slight exaggeration...), my iPod which has it's own dock, my digital camera, my 6 different thumb drives, and my pocket hard drive. These all mount as /dev/sda, /dev/sdb, /dev/sdc... and so on, all depending on the order I plug them in at. This makes my /etc/fstab super messy, and a little unwieldy. It would be nice if my SD card reader always appeared as /dev/sdcardreader and I could mount accordingly there, etc.

Prerequisites
The /dev directory in your linux installation is a list of nodes, and each node corresponds with a piece of hardware on your system. So say you want to read input from the mouse, you'd just read input from /dev/input/mice
Originally the /dev directory stored EVERY SINGLE node that the kernel could possibly know about. This meant that if you used a full kernel with everything enabled, you could have a node for a device you have never even heard of, much less own. This made a bloated and rather lethargic.
Along came devfs, which was a little smart. It populated /dev only with hardware it could find on the system. This meant that if you didn't plug in a mouse, you'd have no /dev/input/mice This was a much better approach because you only had the hardware that was relevant to your system. However, there were still many architectural problems that hadn't been thoroughly thought out fully before implementation, and so as the system grew, the pain was felt.
Enter udev. udev's idea cleared up some of the problems with devfs, including better kernel management (as of kernel 2.6.x), and the implementation of sysfs, which is found at /sys Using sysfs, udev knows about information plugged into your. sysfs can be thought of as a “middle layer” of sorts. When a device is plugged into a system, sysfs provides udev with extensive information about that hardware.

Name Schemes and udev Rule Writing
I have a server I'm running that has a SCSI IDE controller in it. It then has two 74GB hard drives that are mirrored using software mirroring. udev automatically has some of the most generic of rules written in for you already. For instance, for all of my storage devices, udev puts a node record in /dev/disk. To see what udev has found, I just do ls -lR /dev/disk, and it returns this:

drwxr-xr-x 2 root root 160 2006-10-19 13:27 by-id/
drwxr-xr-x 2 root root 180 2006-10-19 13:27 by-path/
drwxr-xr-x 2 root root  60 2006-10-19 13:27 by-uuid/

/dev/disk/by-id:
total 0
lrwxrwxrwx 1 root root  9 2006-10-19 13:27 scsi-20e09e00019ecd40e -> ../../sdb
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 scsi-20e09e00019ecd40e-part1 -> ../../sdb1
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 scsi-20e09e00019ecd40e-part2 -> ../../sdb2
lrwxrwxrwx 1 root root  9 2006-10-19 13:27 scsi-20e09e00019ecd44c -> ../../sda
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 scsi-20e09e00019ecd44c-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 scsi-20e09e00019ecd44c-part2 -> ../../sda2

/dev/disk/by-path:
total 0
lrwxrwxrwx 1 root root  9 2006-10-19 13:27 pci-0000:00:0c.0-scsi-0:0:0:0 -> ../../sda
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 pci-0000:00:0c.0-scsi-0:0:0:0-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 pci-0000:00:0c.0-scsi-0:0:0:0-part2 -> ../../sda2
lrwxrwxrwx 1 root root  9 2006-10-19 13:27 pci-0000:00:0c.0-scsi-0:0:1:0 -> ../../sdb
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 pci-0000:00:0c.0-scsi-0:0:1:0-part1 -> ../../sdb1
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 pci-0000:00:0c.0-scsi-0:0:1:0-part2 -> ../../sdb2
lrwxrwxrwx 1 root root 10 2006-10-19 13:27 pci-0000:00:0c.1-scsi-0:0:6:0 -> ../../scd0

/dev/disk/by-uuid:
total 0
lrwxrwxrwx 1 root root 9 2006-10-19 13:27 6aae5be6-6003-4def-8837-9a6ea3489d5e -> ../../md0


So in /dev/disk are three folders, by-id, by-path, and by-uuid. by-id and by-path both show me the scsi drives by their path or id (big surprise!), but by-uuid doesn't show the drives, but the RAID array they are actually in. I can then reference those drives by any of those address provided there (although RAID arrays are a special case, and you only want to write and read to the array). Notice, however, that these nodes are actually just symlinks to the real node address. This way, if /dev/sda and /dev/sdb are switched on boot one time (for some unknown reason) I can still access the drive by it's id (although a RAID array is still not a perfect example)

udev usually stores the default rules in /etc/udev/rules.d/50-udev.rules (this may differ on your distribution). Look over this file. There are lots of examples of rules in here, you may find that the rule you want to write is already there is some form or another. However, if you want to write your own rules, remember two things: (1) The rules are parsed in “lexical” order, meaning they are ordered by the number at the beginning, and that they must end in .rules It is suggested that you create an /etc/udev/rules.d/10-local.rules and put all your local rules in there. This way, your local rules are parsed before the default rules at 50-udev.rules

The syntax of rules is quite simple. They use a key-value system, and each is separated by commas. They usually have at least one match key and one assignment key. This means one expression to match a device on, and then one one expression that tells udev what to do with it.

Match keys can be one of the following:
  • KERNEL – Match against the name the kernel assigns (ex. “hda” for the first IDE hard drive)
  • SUBSYSTEM – Match against the subsystem of a device (ex. “usb” for USB devices)
  • DRIVER – Match against the actual driver name for a device (ex. “ide-cd” for a CD-ROM)
Assignment keys can be one of the following:
  • NAME – The name of the device node to be used
  • SYMLINK – Creates a symlink to the assigned node name.
On my desktop system, I have a 120GB hard drive for my Linux installation, and a 300GB hard drive for /home directories. So, let's say I want to symlink whatever /dev/sdb (my second drive, Serial ATA), to /dev/home. So I'd just do something like:

KERNEL==”sdb”, SYMLINK+=”home”


Or say we want to easily access our cdrom drive:
KERNEL==”hdc”, SYMLINK+=”cdrom cdrom0”


Now we can get to the cdrom drive from /dev/hdc, /dev/cdrom, or /dev/cdrom0 Isn't that great? Usually, it's a good idea to symlink instead of actually renaming the device in /dev

Getting sysfs Involved
Now, this is all fine and dandy, but guess what? My multicard reader seems to always mount the different card slots as different sdX interfaces. So I can't just symlink /dev/sdc with /dev/cfcard Wouldn't it be nice if we could just look at the model, or manufacturer or something? Well guess what? You can, with sysfs attributes!

sysfs allows you to look at model, size, manufacturer, product information, and a whole lote more (although I don't use anything other than that). Now I can have udev check out the actual device a little more and symlink it based on more than just where the kernel stuck it. However, how can I find the exact information sysfs knows about my device? Easy! udevinfo gives you all the information you need. Start with the following commands:
# find /sys -name sda
/sys/block/sda
# udevinfo /sys/block/sda
Udevinfo starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/block/sda':
	KERNEL=="sda"
	SUBSYSTEM=="block"
	DRIVER==""
	ATTR{stat}=="   14123	 2902   256324	72040	52085   250203  2498104  4701412		0   540648  4773452"
	ATTR{size}=="17930694"
	ATTR{removable}=="0"
	ATTR{range}=="16"
	ATTR{dev}=="8:0"

  looking at parent device '/devices/pci0000:00/0000:00:0c.0/host0/target0:0:0/0:0:0:0':
	KERNELS=="0:0:0:0"
	SUBSYSTEMS=="scsi"
	DRIVERS=="sd"
	ATTRS{ioerr_cnt}=="0x1"
	ATTRS{iodone_cnt}=="0x102bc"
	ATTRS{iorequest_cnt}=="0x102bc"
	ATTRS{iocounterbits}=="32"
	ATTRS{timeout}=="30"
	ATTRS{state}=="running"
	ATTRS{rev}=="0200"
	ATTRS{model}=="ATLAS_V__9_WLS  "
	ATTRS{vendor}=="QUANTUM "
	ATTRS{scsi_level}=="4"
	ATTRS{type}=="0"
	ATTRS{queue_type}=="ordered"
	ATTRS{queue_depth}=="8"
	ATTRS{device_blocked}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:0c.0/host0/target0:0:0':
	KERNELS=="target0:0:0"
	SUBSYSTEMS==""
	DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:0c.0/host0':
	KERNELS=="host0"
	SUBSYSTEMS==""
	DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:0c.0':
	KERNELS=="0000:00:0c.0"
	SUBSYSTEMS=="pci"
	DRIVERS=="aic7xxx"
	ATTRS{broken_parity_status}=="0"
	ATTRS{modalias}=="pci:v00009005d0000005Fsv00009004sd00000053bc01sc00i00"
	ATTRS{local_cpus}=="1"
	ATTRS{irq}=="169"
	ATTRS{class}=="0x010000"
	ATTRS{subsystem_device}=="0x0053"
	ATTRS{subsystem_vendor}=="0x9004"
	ATTRS{device}=="0x005f"
	ATTRS{vendor}=="0x9005"

  looking at parent device '/devices/pci0000:00':
	KERNELS=="pci0000:00"
	SUBSYSTEMS==""
	DRIVERS==""


That's more than enough information about my scsi hard drive in my server. Now I have sysfs specified items that I can use to refer to this drive in my udev rules. So, let's say I want to symlink this hard drive to /dev/backup, because I want to mount it ONLY when it's time to run a backup, and I'll use a script to handle the mount and unmount. So I'm going to create a rule that looks like this:
17930694
SUBSYSTEM==”block”, SYSFS{size}==”17930694”, SYSFS{model}==”ATLAS_V__9_WLS”,SYMLINK+=”backup”


Now, I just restart udev with /etc/init.d/udev restart, and the drive will be all ready for me.

Permissions
Let's take the last rule we wrote and modify it. I only want the group “backup” to be able to read and write to it. In fact, I'll even give the user “backup” ownership to it, as cron will run the script as user “backup” of group “backup” The completed rule would look like this:
17930694
SUBSYSTEM==”block”, SYSFS{size}==”17930694”, SYSFS{model}==”ATLAS_V__9_WLS”,SYMLINK+=”backup”, GROUP=”backup”, OWNER=”backup”


And if I wanted to just chmod it on load, I could just do this:
17930694
SUBSYSTEM==”block”, SYSFS{size}==”17930694”, SYSFS{model}==”ATLAS_V__9_WLS”,SYMLINK+=”backup”, MODE=”0666”



We've only skimmed the surface of udev in this howto. There are environment interactions, and external program manipulations you can learn about, but this howto is long enough. Have fun writing your new udev rules.

Is This A Good Question/Topic? 0
  • +

Replies To: Using udev to Manage Hardware in Linux

#2 rockstar_  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 33
  • View blog
  • Posts: 189
  • Joined: 16-October 06

Posted 26 October 2006 - 10:11 PM

I've noticed some odd caveats with different distributions implementations of udevinfo. For instance, in my Ubuntu Edgy Eft installation, to query a device I had to do the following:

udevinfo -a -p /sys/block/sda



If you have problems, udevinfo should print out a simple list of parameters
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1