Migrating Slackware 8.1 to udev

By Olivier Mehani <shtrom-kb@ssji.net>

Abstract

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.

Introduction

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.

Updates to this document

12/12/2004

07/24/2004

Starting point

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.

Needed upgrades

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

Kernel

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.

Installing hotplug

What is it ?

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.

Prerequisite: usbutils

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

hotplug itself

Nothing more here:

$ make && sudo make install

Init scripts

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 &

udev without hotplug utils

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...

Installing udev

Once again, it is quite simple to install udev:

$ make && sudo make install

Configuring udev

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.

Sample udev.conf

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"

Modifying init scripts

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.

Mounting /sys

We will try to mount the /sys very early in the boot process.

/sbin/mount none /sys -t sysfs

Mounting /sys too

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 :).

Mounting /proc

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

Starting udev

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.

Addon to the rc.S script

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

Defining nodes permissions

The problem

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 solution

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.

The process

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

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.

Sample 10-local.permissions

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

Keeping the same permissions

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

Writing rules

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.

Syntax of a rule

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.

Identification of a device

Actions taken

Both NAME and SYMLINK can be used at the same time.

A rule for my modem

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"

A rule for my CD-writer

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 rule for my USB key

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).

Automatic actions after the node creation

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 dev.d directory

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.

A logging script

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]

Auto mounting of the USB stick

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.

Issues

This part relates several problems I ran into.

NVidia

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

TTY problem on Slackware 10.0

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.

Other resources

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.


Page generated: 2012-04-01T12:11:24+10:00
Source: $Id: udev.xml 66 2010-06-18 04:16:48Z shtrom $
Stylesheet: $Id: page.xsl 68 2010-06-18 05:01:18Z shtrom $