This document describes the (quite simple) steps to follow to get a pure udev Slackware 8.1 system. I suppose this may work on later version of this distro and probably on other flavors of GNU/Linux.
One interesting thing with Linux 2.6 is its support for the sysfs pseudo filesystem, which describes in a coherent way every device the system discovered (a boot time or when hotplugged). udev, is a tool which uses this ability to populate the /dev directory with only the entries needed by your system. Moreover, it can add or remove nodes when a device is hotplugged. The last (but not he least) interest of udev is that you can use it to precisely identify a device and give it a unique device node whatever the moment the system detected it and whatever other devices were plugged in at that time.
This solution avoids the "ok, i plugged my camera when my usbkey was in, so it should be on /dev/sdc1" sport. You can just have a /dev/key and /dev/camera which will be present only when the device is plugged and will always refer to the appropriate device.
By the version of my Slackware, you can guess this is not a freshly installed system. Well, this is true... However, this system worked well enough and I didn't feel the need for a global upgrade. What's interesting with this is that it has no hotplug nor Linux 2.6 support, so I could write a fairly complete (I hope) process to make udev work.
Before compiling and running kernel 2.6, a few things needed to be upgraded...
I had to download the following:
Package | Required by |
---|---|
e2fsprogs-1.35.tar.gz | kernel (>= 1.29) |
hotplug-2004_04_01.tar.bz2 | udev |
module-init-tools-0.9.14.tar.bz2 | kernel |
nfs-utils-1.0.6.tar.bz2 | kernel (>= 1.0.5) |
procps-3.2.1.tar.gz | kernel (>= 3.2.0) |
sysutils-0.1.0.tar.bz2 | could be useful |
usbutils-0.11.tar.gz | hotplug |
udev-030.tar.bz2 |
I won't go too deep into the kernel compilation process as this is not the point of this document. This part describes only the needed steps to get a "udev compliant" kernel. The given choices are for Linux 2.6.5, and may be different for other versions of the kernel.
Configure your kernel as usual, but don't forget to select the following:
Note that sysfs is enabled by default and isn't even appearing in the menus.
With a hotplug-enabled kernel, the insertion of a device generates an event. When one of those is caught, the kernel calls the program specified in /proc/sys/kernel/hotplug (usually /sbin/hotplug) with several parameters. /sbin/hotplug's job is (if necessary) to load the module for the device, upload a firmware to it and the like. Finally, once udev has been installed, hotplug will also call udev so that devices node are created.
In order to work as intended, the hotplug system need usbutils, so it's time to install these:
$ ./configure --prefix=/usr --exec-prefix=/ && make && sudo make install
Nothing more here:
$ make && sudo make install
When booting your computer with devices (cold-)plugged in , they are detected at boot time, but their handling is not done correctly, because hotplug is not launched yet. When installing, a /etc/init.d/hotplug has been created, it solves the problem by detecting and doing the right thing for every cold-plugged device. I just added the following in my /etc/rc.d/rc.local and it works like a charm:
echo "Enabling hotplug... " /etc/init.d/hotplug start 2>&1 >/dev/null &
It is said that one has the possibility to use udev without the hotplugs utils. The program path in /proc/sys/kernel/hotplug should be changed (for example in the init scripts) to /sbin/udev:
# echo "/sbin/udev" > /proc/sys/kernel/hotplug
I haven't tried this myself, so I don't know more...
The configuration files and directories for udev can be found in /etc/udev.
Rules and permissions are seen below. The only modification to make in udev.conf is to change the udev_root from /udev (default) to /dev. However, I would advice to make tests with devices created in /udev at first, and when all seems to be reasonnably working, to make the modification to have a pure udev system. This may avoid problems.
My own udev.conf currently looks like this:
udev_root="/dev/" udev_db="/dev/.udev.tdb" udev_rules="/etc/udev/udev.rules" udev_permissions="/etc/udev/udev.permissions" default_mode="0600" default_owner="root" default_group="root" udev_log="yes"
We want udev to be in use automatically, so it is necessary to do several things at boot time. Moreover, we will do this as soon as possible so that every other application will work with nodes created by udev and not the old static ones. I chose to do the whole work in the /etc/rc.d/rc.S script, just after the root filesystem check and its being remounted read-write.
We will try to mount the /sys very early in the boot process.
/sbin/mount none /sys -t sysfs
Another solution would be to add a line to /etc/fstab:
none /sys sysfs defaults 0 0
And then use the following command to mount the pseudofilesystem:
/sbin/mount -a -t sysfs
Both solutions are acceptable, I chose the first one out of laziness :).
The proc filesystem is usually mounted later, but udev needs it, so we have to mount it right after having mounted /sys:
/sbin/mount -a -t proc
Once all the above steps have beend followed, udev can be launched. In the extras directory of the udev source tree can be found a script called start_udev. You can copy it, for example to you /etc/rc.d directory and call it.
Finally, I added this to my /etc/rc.d/rc.S script, just after the /sbin/mount -w -o remount / line:
if [ "`uname -r | cut -f 1,2 -d .`" = "2.6" ]; then # Check that we're really running a 2.6 kernel /sbin/mount none /sys -t sysfs /sbin/mount -a -t proc /etc/rc.d/start_udev fi
By default, all devices nodes created by udev get the same permissions and owner, as defined in udev.conf. However, it may be appreciable that some devices get special rights, for example, it should be a good idea that the CD-writer device be owned by a certain group, to which would belong user with rights to burn CDs. Because the nodes are dynamically created and removed, it would be useless to change the rights on these files manually.
The /etc/udev/udev/permissions.d directory's content is here to solve the problem: the admin can enter the permissions for specific /dev entries in several files. The /etc/udev/permissions.d/50-udev.permissions file contains the default permissions, it came with udev.
When creating a new node, udev goes through every file in the permissions.d directory until it finds the permissions it should set for the node. The files are read in lexical order and udev stops as soon as it found a matching entry. This way, it is possible to override the defaults (50-udev.permissions) simply by putting a file which lists the correct permissions for each entry and which name comes lexicographically earlier (for example 10-local.permissions).
The syntax in the permissions files is quite simple:
nodename:owner:group:permissions
Any of the owner, group or permissions fields can be empty. The default value for the field will be used in that case. Some wildcards and simple regexps may be used in the nodename field.
After using several systems with udev, I came up with the need of these permissions. The list may not be complete, but I don't get weird behavior anymore when doing not so usual things.
#name:user:group:mode null::sys:0666 full::sys:0666 zero::sys:0666 random:::0644 urandom:::0644 rtc:::0444 ptmx:::0666 tty::tty:0666 console::tty:0622 vcs*::tty:620 ttyS*::uucp:0660 event*:::644 js*:::644 mice*:::644 mouse*:::644 kmem::kmem:640 mem::kmem:640 port::kmem:640 fd*::floppy:660 hd*::disk:660 sr*::disk:660 nst*::disk:660 nqft*::disk:660 nzqft*::disk:660 qft*::disk:660 st*::disk:660 zqft*::disk:660 lp*:lp:lp:660 audio:root:audio:660 dsp*:root:audio:0660 mixer*:root:audio:0660 sequencer*:root:audio:0660 lirc*:root:lirc:0660 video*:root::0666 cdrom::disk:660 burner::cdwrite:0660 usbkey*::disk:660
While working with my permissions, I wanted udev to set the permissions as they were before. I came up with this little (bash) one-liner which compares the older /dev contents' permissions with /udev's and displays the differences so that I could correct these.
$ for file in `ls /udev/` ; do ls -l /dev/${file} ; done | grep -v -- "------- *1 root *root" | less
Here we come to the interesting part of udev: writing nodes renaming/symlinking rules. When udev get called upon the insertion of a new device, it checks the files in /etc/udev/rules.d (as for /etc/udev/permissions.d) for a matching rule. A file called 50-udev.rules comes by default.
A rule consist of several, comma seperated, fields. Every field is of the form KEYWORD="something". There are two types of keywords :
Following is a short description of the above keywords. Note that there are more of them that can be used, and a much more advanced usage of them can be done, refer to the manpage of udev(8) for details.
Both NAME and SYMLINK can be used at the same time.
My modem is plugged on /dev/ttyS1. I used to have a /dev/modem symbolic link to it, but I can't just hand create it with udev. Let's write a simple rule:
KERNEL="ttyS1", SYMLINK="modem"
I want my CD-writer to be referred to as /dev/burner, but, for compatibility reasons, to be still accessible by /dev/sr0. This is quite a dummy example as my CD-writer is not hot-pluggable, but udev has to handle every device, not only hot-pluggable ones. First of all, I want to gather information about my drive. It is still referred to by the system as sr0, so let's look for it in /sys:
$ find /sys/ | grep sr0 /sys/block/sr0 /sys/block/sr0/queue /sys/block/sr0/queue/iosched /sys/block/sr0/queue/iosched/write_batch_expire /sys/block/sr0/queue/iosched/read_batch_expire /sys/block/sr0/queue/iosched/antic_expire /sys/block/sr0/queue/iosched/write_expire /sys/block/sr0/queue/iosched/read_expire /sys/block/sr0/queue/iosched/est_time /sys/block/sr0/queue/nr_requests /sys/block/sr0/device /sys/block/sr0/stat /sys/block/sr0/size /sys/block/sr0/range /sys/block/sr0/dev
OK ! It exists in sysfs, good news. Next step is to extract the information from sysfs:
$ udevinfo -a -p /sys/block/sr0 udevinfo starts with the device the node belongs to and then walks up the device chain, to print for every device found, all possibly useful attributes in the udev key format. Only attributes within one device section may be used together in one rule, to match the device for which the node will be created. looking at class device '/sys/block/sr0': SYSFS{dev}="11:0" SYSFS{range}="1" SYSFS{size}="2097151" SYSFS{stat}=" 0 0 0 0 0 0 0 0 0 0 0" follow the class device's "device" looking at the device chain at '/sys/devices/pci0000:00/0000:00:10.0/0000:02:05.0/host0/0:0:0:0': BUS="scsi" ID="0:0:0:0" SYSFS{detach_state}="0" SYSFS{device_blocked}="0" SYSFS{model}="MP6200S " SYSFS{online}="1" SYSFS{queue_depth}="2" SYSFS{rev}="2.20" SYSFS{scsi_level}="3" SYSFS{type}="5" SYSFS{vendor}="RICOH " looking at the device chain at '/sys/devices/pci0000:00/0000:00:10.0/0000:02:05.0/host0': BUS="" ID="host0" SYSFS{detach_state}="0" looking at the device chain at '/sys/devices/pci0000:00/0000:00:10.0/0000:02:05.0': BUS="pci" ID="0000:02:05.0" SYSFS{class}="0x010000" SYSFS{detach_state}="0" SYSFS{device}="0x0001" SYSFS{irq}="18" SYSFS{subsystem_device}="0x0000" SYSFS{subsystem_vendor}="0x0000" SYSFS{vendor}="0x1000" looking at the device chain at '/sys/devices/pci0000:00/0000:00:10.0': BUS="pci" ID="0000:00:10.0" SYSFS{class}="0x060400" SYSFS{detach_state}="0" SYSFS{device}="0x7448" SYSFS{irq}="0" SYSFS{subsystem_device}="0x0000" SYSFS{subsystem_vendor}="0x0000" SYSFS{vendor}="0x1022" looking at the device chain at '/sys/devices/pci0000:00': BUS="" ID="pci0000:00" SYSFS{detach_state}="0"
The interesting part (concerning my burner) is the one about the device at '/sys/devices/pci0000:00/0000:00:10.0/0000:02:05.0/host0/0:0:0:0' It contains several interesting fields such as model or vendor. That's enough to clearly identify my device, as I have only one of this kind. In the case of several devices of the same model, It would be interesting to match these according to their serial number (if it exists, which doesn't seems to be the case of my CD-burner). So I want the device with SYSFS{model} equal to "MP6200S" (the trailing spaces can be safely removed) and (for safety, but this is useless) which kernel "standard" device nodename is srsomething to be renamed to /dev/burner and to have a symlink with the original name pointing to it. The rule would be:
SYSFS{model}="MP6200S", KERNEL="sr*", SYMLINK="%k", NAME="burner"
%k is a shorthand for the device's orgininal nodename.
A better example to demonstrate the power of udev is for my USB memory stick. It can be plugged or unplugged when the computer is on or off, and, depending on what is plugged at the time (an USB printer, an SCSI driver, another memory stick,...) the device node may be different (/dev/sda, /dev/sdb, ...). I want a device node referring to it to always be /dev/usbkey and I want the existing partitions (the non existing partition won't even show up) to be named appropriately. After having plugged my USB stick, I first take a look at the output of dmesg to know the "standard" name the kernel gave to the device:
$ dmesg | tail -13 Initializing USB Mass Storage driver... scsi1 : SCSI emulation for USB Mass Storage devices Vendor: USB Mass Model: Storage Drive 2 Rev: 002 Type: Direct-Access ANSI SCSI revision: 02 SCSI device sda: 251904 512-byte hdwr sectors (129 MB) sda: assuming Write Enabled sda: assuming drive cache: write through sda: sda4 Attached scsi removable disk sda at scsi1, channel 0, id 0, lun 0 Attached scsi generic sg1 at scsi1, channel 0, id 0, lun 0, type 0 USB Mass Storage device found at 2 drivers/usb/core/usb.c: registered new driver usb-storage USB Mass Storage support registered.
So the device is sda. Now we will try and gather informations from sysfs (truncated to the relevant parts):
$ find /sys/ | grep sda /sys/block/sda (...) $ udevinfo -a -p /sys/block/sda udevinfo starts with the device the node belongs to and then walks up the device chain, to print for every device found, all possibly useful attributes in the udev key format. Only attributes within one device section may be used together in one rule, to match the device for which the node will be created. (...) looking at the device chain at '/sys/devices/pci0000:00/0000:00:08.2/usb1/1-1': BUS="usb" ID="1-1" SYSFS{bConfigurationValue}="1" SYSFS{bDeviceClass}="00" SYSFS{bDeviceProtocol}="00" SYSFS{bDeviceSubClass}="00" SYSFS{bMaxPower}=" 84mA" SYSFS{bNumConfigurations}="1" SYSFS{bNumInterfaces}=" 1" SYSFS{bcdDevice}="0100" SYSFS{bmAttributes}="80" SYSFS{detach_state}="0" SYSFS{idProduct}="0117" SYSFS{idVendor}="0117" SYSFS{manufacturer}="Power by USB" SYSFS{product}="USB BAR " SYSFS{serial}="A0716141662" SYSFS{speed}="480" (...)
Two things to note here: first model is now a product one and a serial field exists, quite interesting if I have friends who have the same model of USB key. Finally, the rule would be:
SYSFS{product}="USB BAR", SYSFS{serial}="A0716141662", KERNEL="sd?*", SYMLINK="%k", NAME="usbkey%n"
sd?* matches every node name starting with sd, a single character, and zero or more trailing ones. In fact it will match sda5, sdc, sdb1, and so on, that is, the nodes I know my USB stick may be given. %n is a shorthand for the number of the node (6 for sdc6 for example).
Another interesting thing is that, after the node creation, udev can run several scripts automatically. This can be useful, for example, upon insertion of a new network device, to launch the DHCP client, or simply mount my USB stick.
The scripts to be executed after reside in /etc/dev.d. For every device, the scripts in the subdirectory default will be executed, provided their name ends in .dev (this allows to store scripts that you don't want to run). Then, scripts can be called for certains devices only. Two other directories are scanned for scripts to execute : /etc/dev.d/DEVNAME and /etc/dev.d/SUBSYSTEM. SUBSYSTEM is the name found in the sysfs directory (such as block or scsi_generic). DEVNAME is the name of the (renamed) node.
In /etc/dev.d/default/log.dev, I have the following:
#!/bin/bash if [ "$ACTION" == "add" ] ; then logger -t dev.d "New device added ($*)[`env`]" fi
Upon every device insertion, I write a line to the log containing the script's arguments and environement:
Apr 16 22:44:33 shmoldu dev.d: New device added (block)[PWD=/ ACTION=add DEVPATH=/block/sda/sda4 DEVNAME=/dev/usbkey4 SHLVL=1 _=/usr/bin/env]
The device for my USB stick is usbkey, and I want to mount partition 4 (/dev/usbkey4) in /key. I added the following to my /etc/fstab:
/dev/usbkey4 /key vfat exec,noauto,users 0 0
And this script as /etc/dev.d/usbkey4/mount.dev:
#!/bin/bash if [ "$ACTION" == "add" ] ; then logger -t dev.d "Mounting /key" /bin/mount /key fi
That's all, it works well.
When using NVidia's own graphic driver with udev, one encounters a problem with X complaining that it can't find the node. In fact it is due to the system not creating the node quick enough after X's probing the driver. There are two solutions to this problem.
The first one is simply creating, at boot time, the needed nodes. This is not the cleanest method but it works.
/bin/mknod /dev/nvidia0 c 195 0 /bin/mknod /dev/nvidiactl c 195 255
The second, cleaner, method consits in probing the module at boot time (which seems to be what NVidia advises). A little addition to my /etc/rc.d/rc.modules has been sufficient:
/sbin/modprobe nvidia
I finally upgraded my system to version 10 of the distro. It natively supports udev and comes with a (big !) set of predefined rules.
One of these rules concerns the /dev/tty entry. It used to be a single file, corresponding to the controlling terminal of the process accessing it. But a rule in /etc/udev/rules.d overrides this:
KERNEL="tty[p-za-e][0-9a-f]*", NAME="tty/s%n", SYMLINK="%k"
On my setup this preventend several terminal emulators like xterm or Gnome's one to start. The problem has been fixed by changing the above line to:
KERNEL="tty[p-za-e][0-9a-f]*", NAME="ttys/s%n", SYMLINK="%k"
Note the new "s" in the NAME entry. This way, the "normal" /dev/tty is properly created.
Before migrating my system to udev, I had to read some documentation, moreover, I tried in this document to have a rather global view of the entire process. To get more precise information, you can refer to these other sites.