HAL and device management

[Apart from a few corrections, this is the same as an email I sent in April 2006 to a mailing list of friends.]

Update (2011-04-21) This is largely here for historical interest now, since HAL is being deprecated by all the major distributions.

I hope these notes might be of some use to the rest of you, since this stuff basically is a pain in the arse. When I floated the idea before, people said things like ‘i'd love a post on ‘what the fuck hald thinks it's for’” so here you go... There may be many of mistakes in this email, since this software is all complex and rapidly changing, so apologies in advance for that.

Basically, the problem I wanted to solve was for removable storage devices to be automatically mounted when I plug them into my computer and for them to be mounted at a predictable point (of my choosing) in the filesytem. This was mostly not for my own benefit, FWIW, but it turns out to be pleasant when it's working nonetheless.

Assuming you're using a late 2.6 Linux kernel¹ then your options for this seem to be:

¹ Even in very late versions of 2.6, like 2.6.13, lots of these things don't work - when I say late I mean at least 2.6.15...
  1. The traditional approach: plug in the device, tail /var/log/syslog to find the device name and manually mount the device. The disadvantages of this are that it doesn't work for people who don't want to use the terminal or remember how to mount devices.

  2. A similar, but slightly simpler approach: make entries in fstab that refer to the /dev/disk symlinks that you get with udev and late 2.6 kernels, e.g. :

    /dev/disk/by-id/usb-MINOLTA_MINOLTA_DIMAGE_X_000035204086-part1 /mnt/camera     vfat    user,noauto        0       0
    /dev/disk/by-uuid/135D-2610                                     /mnt/ipod       vfat    user,noauto,sync   0       0
    

    (I can't remember which versions this started working properly with, but kernel 2.6.15.2 and udev 0.084 are certainly OK.)

    This isn't too bad a solution, in fact - it even gets around the requirement that you need to use the terminal if you're using Gnome, since Nautilus's computer folder adds icons for all the /etc/fstab mount points and you can mount and unmount with the context menu.

    Update: If you are having problems finding the UUID of a device (e.g. your system doesn't create the by-uuid symlinks) then you can display them with blkid (from e2fsprogs) or /lib/udev/vol_id. Thanks to Alexander Amelkin for pointing this out.

  3. Alternatively, you can use an ungodly combination of udev, hald, dbus, gnome-volume-manager and friends to get this functionality. If you're lucky² and you use Gnome, then you will have noticed some of this working already when folder windows pop up when you plug in USB mass storage devices, DVDs are autoplayed, etc.

    ² Depending on your point of view, I suppose...

So, let's assume that, like me, you want to go down route (c) since it's interesting to see whether this all works nowadays. Some cursory exploration will lead you to lovely diagrams like this:

http://webcvs.freedesktop.org/*checkout*/hal/hal/doc/spec/hal-spec.html#ov_hal_linux26
[Alternative URL]

... which should provoke a certain amount of hollow laughter if you're a fan of approach (a). Also, AFAICT that diagram is totally incorrect these days. For example, /sbin/hotplug is now deprecated and its functionality absorbed into udevd - you should make sure you have the hotplug package purged as well as removed from your Debian system. Also hal.hotplug is replaced by unix domain socket communication from udevd to hald. And Debian systems don't use the fstab-sync bit at all - it's replaced by pmount. So there isn't much in that diagram that applies, except perhaps the “Linux 2.6 kernel” box in the bottom left. ;)

sysfs, which should be mounted on /sys, has the canonical representation of devices in your computer (devfs was thrown out before 2.6 for good reasons.) sysfs has a very clean layout compared to /proc or a traditional /dev as you'll see if you poke around in there. Particularly of note are the files with leafname “dev”, which contain the major and minor device numbers of the device and “uevent”. If you write the correct string to a uevent file the kernel generates an event as if the device had just been plugged in, so that udev (or whatever) can create devices in /dev in a consistent fashion across all devices, both fixed and removable. (This isn't quite the case yet, though; some devices are still created manually by the init scripts.)

AFAICT, sysfs is the only bit which is more-or-less guaranteed to be the same across different distributions. Everything else is now done in userspace. If you're interested in device creation and removal events then you listen to a kernel netlink socket. (This has changed somewhat since 2.6.14 - userspace used to be notified by invoking the binary named in /proc/sys/kernel/hotplug, which has in the past been /sbin/hotplug and /sbin/udevsend...)

So, from this point on, assume that everything else in this email is Debian specific. When you boot up, then one of the first init scripts (/etc/rcS.d/S03udev) does the following (missing out some hackery):

udevd basically checks all the events it receives against rules in /etc/udev and does various things for matching rules, such as creating a /dev entry for newly created devices. This does have to advantage of keeping your /dev directory down to a reasonable size compared to the MAKEDEV approach. (It also does helpful things like create the /dev/disk/by-label/ symlinks and so on.)

HAL gets all its information from udevd via one of these rules. When the hal package is installed it adds various rules files to /etc/udev which basically tell udevd to copy everything to HAL:

RUN+="socket:/org/freedesktop/hal/udev_event"

(Contrary to what you might read elsewhere, hald doesn't talk to the kernel directly using netlink. I think it used to do this in order to find when devices were mounted and unmounted, but nowadays it does that by polling /proc/mounts.)

You might have noticed that the original question (i.e. “what the fuck hald thinks it's for”) is still unanswered. HAL basically maintains its own database of devices connected to your computer, but gets into the business of guessing what devices are likely to be cameras, which are iPoda, etc. You can see all the devices it knows about with lshal in a terminal or using a GUI like this:

http://webcvs.freedesktop.org/*checkout*/hal/hal/doc/spec/hal-devices1.png

(That's hal-device-manager, which is in its own Debian package of the same name, and found in the standard Gnome setup in Desktop -> Administration -> Device Manager.)

If you're interested in changes to this device hierarchy (as the gnome-volume-manager and presumably the KDE equivalent are) then you talk to hald via D-BUS. There's more detail about that, and an example Python program here:

http://webcvs.freedesktop.org/*checkout*/hal/hal/doc/spec/hal-spec.html#dbus-api

... and another simple example here:

http://lists.freedesktop.org/archives/hal/2005-January/001623.html

In case you're unaware of it, D-BUS is what the cool kids are using for IPC these days, as you'll see if you try to remove the libdbus-1-2 package from your system.

http://www.freedesktop.org/wiki/Software/dbus
http://dbus.freedesktop.org/doc/dbus-tutorial.html

HAL has its own namespace for devices in a filesystem-like hierarchy under /org/freedesktop/Hal/devices/. I suspect that the objects in this hierarchy have a one-to-one mapping to D-BUS objects, rather than this just being a case of over-generalized-namespace-itis, but it's not really clear from the documentation I've read. Possibly someone who knows about D-BUS can tell me? Each of the objects in this hierarchy has a series of properties whose key names are in their own dot-separated namespace, and whose values have a very simple type system. (You can see examples in the hal-device-manager image linked above.)

The HAL package also has large pile of carefully crafted XML files full of rules and device information; udev doesn't care about any of this stuff (and quite right too, since it's obviously a nightmare.)

A common misconception about HAL is that it actually handles the mounting and unmounting of devices itself. In fact this is done by gnome-volume-manager, which listens for HAL events via D-BUS and then invokes pmount-hal to mount devices. (Or it might not mount them at all - it depends if you've configured that to happen via Desktop -> Preferences -> Removable Drives and Media:

a screenshot of gnome-volume-properties

... which is gnome-volume-properties, the configuration GUI in the gnome-volume-manager package. The difference between a device being “inserted” and “hotplugged” is left as an exercise for the reader, since I don't know and I'm sure the answer will cause pain and gnashing of teeth.)

This means that HAL maintains an object representing the device throughout the time that it's plugged in, but when you log in and out of Gnome the removable devices currently inserted are mounted and unmounted, and become owned by whatever user is logged in. (pmounted drives, incidentally, don't appear in /etc/mtab, only in /proc/mounts, so if you want to manually unmount them then you have to use pumount rather than umount. Another point that may be helpful is that on Debian you have to be a member of the plugdev group for pmount to work.)

So, to return to my original aim, how do I get my iPod to appear at a consistent point of my choosing in the filesystem? The default for such devces is to be mounted at /media/<VOLUME-LABEL> or /media/MARK'S IPOD in my case. That name causes problems for many pieces of distinctly average software, so I wanted to change it to /media/ipod. In order to do this you add rules to one of the .fdi files that HAL looks at. In this case, I edited /etc/hal/fdi/policy/preferences.fdi (although you could add a new fdi file and that would work fine) and added the following after the <deviceinfo version="0.2"> line:

  <device>
    <match key="info.udi" string="/org/freedesktop/Hal/devices/volume_uuid_E265_5E6A">
      <merge key="volume.policy.desired_mount_point" type="string">ipod</merge>
      <merge key="volume.policy.mount_option.iocharset=iso8859-15" type="bool">true</merge>
      <merge key="volume.policy.mount_option.sync" type="bool">true</merge>
    </match>
  </device>

In that case I'm matching on the udi (“unique device identifier”) which according to the HAL spec is “computed using bus-specific information and is meant to be unique across device insertions and independent of the physical port or slot the device may be plugged into”. However, you can match on any of the properties in the hierarchy and nest matches, etc. I should probably make my matching smarter so that it would also match the iPod when I plug it into a firewire port. The desired_mount_point option is appropriately named since, of course, it's only a recommendation from HAL to whatever system might want to deal with mounting and unmounting the volume. I've had varying success with the mount_option properties. The sync option above does seem to be dealt with correctly, but the iocharset one isn't. It looks highly suspicious, since surely the key should be “volume.policy.mount_option.iocharset” and the value of type string, but apparently this is correct (or has been at some point in the past):

http://lists.freedesktop.org/archives/hal/2005-January/001901.html

A quick look at the source suggests that pmount-hal currently ignores most ofthe mount_option properties. :(

So, this system works reasonably well for me now. As far as setting this up on your own system goes, I think you'll need at least the following packages installed:

  apt-get install udev \
                  pmount \
                  hal \
                  gnome-volume-manager \
                  dbus \
                  hal-device-manager

... in at least the current “testing” versions. One useful tip is that hald's --use-syslog option isn't documented in the Debian man page, and without it you can't see anything that hald is up to. (I've submitted a bug about this.) Update: As the message from hald --help suggests, --use-syslog only works if you also have --verbose=yes.

So, this all vaguely works for me now. There lots of niggling problems still that I won't go into, but it's all worth it to have a little iPod icon pop up on your desktop when you plug it in ;)

iPod and removable disc icons

... the other drive with the USB logo is a bog-standard removable hard disk.

I hope this nightmarish email is of some interest to you. These documents are also useful on these topics:

Since the only parts of this system that are really fixed by the kernel are the sysfs interface and the netlink socket, I wonder how many alternatives people have built replacing each component in this rather byzantine architecture? For example, it's certainly possible to conceive of much simpler systems than HAL that will do the basic things that users like myself want to do in response to the events from udevd. Equally, if you were so-inclined you could replace the udevd part with something else - if anyone knows of any such attempts, I'd be interested to hear about them.