August 2, 2006

Triggering software events from hardware changes with HAL and Ivman

Author: Manolis Tzanidakis

Traditionally on a Unix-like system like GNU/Linux you must mount a device such as a CD-ROM before being able to use it. This is one of the biggest complaints from newcomers, especially Windows refugees. The latest versions of the GNOME and KDE desktop environments offer automounting functionality, and some distributions (such as Ubuntu and its derivatives) have it enabled by default. If you choose not to use any of these environments, here's an alternative solution.

Ivman is a daemon that was originally designed for automounting removable devices. Nowadays it offers more than that; it can run commands upon system events -- for example, shut down the system when the power button is pressed, or run a file manager when a portable MP3 player is connected. It relies on the Hardware Abstraction Layer API, which identifies the hardware and probes it continually for events such as CD-ROM insertion and ejection.

Most major GNU/Linux distributions offer pre-built packages for HAL and Ivman. A recent Linux kernel (2.6.13 or later) and UDEV are also required. I've tested Ivman on Debian (Testing and Unstable), Gentoo, and Arch Linux without problems. Slackware does not offer packages for HAL, but you can install packages built by various GNOME distributions for Slack -- both GWare and Freerock have HAL included -- and then install Ivman from source with the well-known ./configure; make; make install commands.

Ivman can run either as a system-wide daemon with its configuration residing in /etc/ivman, or by an individual user, in which case it reads its configuration from $HOME/.ivman. Ivman uses an XML configuration file format. Every sentence starts with a bracket, which to be valid must be closed, as in <blah blah />. To enable or disable an option, use the Booleans true or false. Comments start with <!-- and end with -->.

The utility uses four configuration files. IvmConfigBase.xml defines runtime options, such as verbose debug output, whether the daemon should fork in the background, the user/group to run the daemon as, and mount/umount commands and options. To be able to access the mounted devices you should add your non-root user (in Debian and Gentoo it's ivman) to the group that Ivman runs as (in Debian and Gentoo it's plugdev) by using a command like usermod -a -G plugdev ivman.

IvmConfigActions.xml instructs the daemon to perform certain actions when media is inserted. This file is used for enabling automount or disabling it for specific devices, and also for configuring autoplay actions for devices such as audio CDs and video DVDs. One problem I had was that my USB flash stick was mounted as a block device (/dev/sdb) instead of as a valid partition (/dev/sdb1). To overcome this I disabled mounting this block device with the following lines:

<!-- don't mount usbstick as /dev/sdb -->
<ivm:Match name="hal.block.device" value="/dev/sdb">
	<ivm:Option name="mount" value="false" />
</ivm:Match>

By default all devices are mounted as /media/DEVICE_NAME -- e.g. /media/sdb1 -- but you can change this by adding an appropriate line to /etc/fstab, like:


/dev/sdb1 /mnt/usbstick vfat noauto,noatime,sync,users 0 0

Some versions of HAL don't report USB disks as mountable by default. If you find that, copy the following as /etc/hal/fdi/policy/mountpolicies.fdi:

<?xml version="1.0" encoding="ISO-8859-1"?>
<deviceinfo version="0.2">

  <device>
    <match key="info.category" string="storage">
      <match key="storage.bus" string="usb">
        <merge key="storage.policy.should_mount" type="bool">true</merge>
      </match>
    </match>

  </device>
</deviceinfo>

Then stop and restart the HAL daemon, hald.

The IvmConfigConditions.xml file describes actions to be performed when devices emit conditions. I use this to shut down my laptop when the power button is pressed and to hibernate it when I close the lid with the following lines:

<ivm:Match name="hal.info.udi" value="/org/freedesktop/Hal/devices/acpi_PWRB">
	<ivm:Condition name="ButtonPressed" exec="/usr/bin/sudo /sbin/init 0" />
</ivm:Match>

<ivm:Match name="hal.info.udi" value="/org/freedesktop/Hal/devices/acpi_LID0">
	<ivm:Condition name="ButtonPressed" exec="/usr/bin/sudo /usr/sbin/hibernate" />
</ivm:Match>

To enable this you must also install sudo and configure it to allow the ivman user to run these commands as root without requiring a password. To do this, run visudo as root and add the following lines (replace the part in italics to match your setup):

IVMAN_USER HOSTNAME=(root) NOPASSWD: /sbin/init, /usr/sbin/hibernate

IvmConfigProperties.xml instructs the daemon to run certain commands when device properties change -- for example, when a device is mounted or the battery is used. I use this to adjust the brightness on my laptop's screen depending on whether it runs on battery or not and also enable or disable laptop mode accordingly:

<ivm:Match name="hal.info.udi" value="/org/freedesktop/Hal/devices/acpi_BAT0">
	<ivm:Property name="hal.battery.rechargeable.is_discharging">
		<ivm:Action value="true" exec="/usr/bin/sudo /usr/sbin/laptop_mode auto" />
		<ivm:Action value="true" exec="/usr/bin/smartdimmer -s 11"/>
		<ivm:Action value="false" exec="/usr/bin/sudo /usr/sbin/laptop_mode auto" />
		<ivm:Action value="false" exec="/usr/bin/smartdimmer -s 15"/>
	</ivm:Property>
</ivm:PropertiesConfig>

Sudo must be configured to allow the Ivman user to run laptop mode. To see what devices HAL has found on your system you should run lshal --long | less. To monitor devices for changes, run lshal --monitor. This command can be used to find out how your system reports special events, and you can configure Ivman accordingly.

All configuration files include instructions and examples in comments, and man pages are also provided for more information. You can also find some more examples of how to make use of Ivman in a How-to in Gentoo Wiki.

Click Here!