Scripting HAL

Recent releases of Fedora and other GNU/Linux distributions include a Hardware Abstraction Layer (HAL) which is used to support device plug-and-play capabilities.  In this post I will show you how your shell scripts can use HAL to retrieve device and system information.

The term HAL is overloaded as it used to refer both to a specification and the actual software which implements the specification.  From an application developers viewpoint, HAL is way to enumerate the capabilities and features of hardware attached to a system and receive notification when something about the hardware changes.

First, a very quick overview of HAL.  Each item of physical hardware in a computer is regarded as being an device object which identified by a Unique Device Identifier (UDI).  Associated with each device object is a variable set of well-defined typed key-value pairs (or metadata) called device properties which describe what each device object represents together with its properties.  Some device properties are derived from the actual physical hardware, some are merged from XML-formatted files, known as Device Information Files, and some are derived from the actual device configuration.  Mandatory device properties are defined in the HAL specification.



A HAL daemon is used to maintain and update the list of device objects forming a hardware configuration and is notified when devices are added or removed.  HAL also provides callbacks so that the operating system and applications can react to hardware configuration changes in order to maintain system policy.  An example of such a callback and policy is when you plug in a USB memory stick HAL automatically creates a mount point and mounts the device.

For a good backgrounder on HAL, see this article Making Hardware Just Work by Havoc Pennington.  For more detailed information, you should read the HAL Specification and the HAL project page at freedesktop.org.

A number of command line utilities are provided for accessing and manipulating device objects.



















hal-disable-pollingdisable polling on drives with removable media
hal-find-by-capabilityfind device objects by capability matching
hal-find-by-propertyfind device objects by property matching
hal-get-propertyget a property from a device object
hal-is-caller-locked-out  determine if caller is locked out
hal-is-caller-privilegeddetermine if caller is privileged
hal-locklock an interface
hal-set-propertyset a property on a device object
lshallist devices objects and properties

For the purposes of this post, we are only interested in a small number of the above utilities, i.e. those utilities which locate and return information about device objects.

Here is a simple use of the hal-find-by-property utility to retrieve a list of network interfaces.
$ hal-find-by-property --key linux.subsystem --string net
/org/freedesktop/Hal/devices/net_3a_67_56_92_fb_73
/org/freedesktop/Hal/devices/net_computer_loopback
/org/freedesktop/Hal/devices/net_00_1c_c0_6d_8a_f7
The HAL specification specifies a system namespace which contains information about a system and it's currently running kernel.  The following script retrieves certain information from the system namespace.
udi=$(hal-find-by-property --key info.product --string Computer)
printf "Board Manufacturer: $(hal-get-property --udi $udi --key system.board.vendor)\n"
printf " Model: $(hal-get-property --udi $udi --key system.board.product)\n"
printf " Version: $(hal-get-property --udi $udi --key system.board.version)\n"
printf " Serial No.: $(hal-get-property --udi $udi --key system.board.serial)\n"
printf " Release Date: $(hal-get-property --udi $udi --key system.firmware.release_date)\n"
printf " Bios Version: $(hal-get-property --udi $udi --key system.firmware.version)\n"
printf " Motherboard UUID: $(hal-get-property --udi $udi --key system.hardware.uuid)\n"
Here is the output from the computer which I used to write this post.
Board Manufacturer: Intel Corporation
Model: DX48BT2
Version: AAE26191-205
Serial No.: BQBQ8280004G
Release Date: 08/07/2008
Bios Version: BTX3810J.86A.1814.2008.0807.2334
Motherboard UUID: 2237F666-4F75-11DD-894F-0007E9747DB3
The next example retrieves certain information about all the storage devices on a system using the volume capability.  The volume namespace is for device objects that represent storage devices with a filesystem that are mountable.  Such device objects have the capability volume.
for udi in $(/usr/bin/hal-find-by-capability --capability volume)
do
mount=$(hal-get-property --udi $udi --key volume.mount_point)
device=$(hal-get-property --udi $udi --key block.device)
label=$(hal-get-property --udi $udi --key volume.label)
fstype=$(hal-get-property --udi $udi --key volume.fstype)
echo "$device $mount $fstype $label"
done
Here is sample output from this script.
/dev/sda1  /boot      ext3  /boot
/dev/sdb2 /abac ext3 /abac
/dev/sdb1 /home/fpm ext3 fpm
/dev/sda2 LVM2_member
/dev/sdc1 LVM2_member
/dev/sdc2 LVM2_member
You can also drill down and return information about specific devices such as an optical drive, i.e. device objects which have a capability of cdrom.
for i in $(hal-find-by-property --key storage.drive_type --string cdrom)
do
printf "Manufacturer: $(hal-get-property --udi $i --key storage.vendor)\n"
printf " Model: $(hal-get-property --udi $i --key block.device)\n"
printf " Bus: $(hal-get-property --udi $i --key storage.model)\n"
printf " Path: $(hal-get-property --udi $i --key storage.bus)\n"
done
Here is the output for the computer which I am using to write this post.  It contains a single DVD RW drive.
Manufacturer: HP
Model: /dev/sr0
Bus: DVD Writer 1070d
Path: pci
In many cases there are multiple ways of retrieving the information you want.  For example, here is another way of retrieving the same information, together with details of the volume label and mount point if there is a disk in the drive, using the storage.cdrom capability.
for udi in $(/usr/bin/hal-find-by-capability --capability storage.cdrom)
do
device=$(hal-get-property --udi $udi --key block.device)
vendor=$(hal-get-property --udi $udi --key storage.vendor)
model=$(hal-get-property --udi $udi --key storage.model)
if [[ $(hal-get-property --udi $udi --key storage.removable.media_available) = true ]]
then
parent_udi=$(hal-find-by-property --key block.storage_device --string $udi)
mount=$(hal-get-property --udi $parent_udi --key volume.mount_point)
label=$(hal-get-property --udi $parent_udi --key volume.label)
fi
printf "$vendor $model $device $mount $label\n"
done
Here is the output from this script when there is a DVD in the drive followed by when there is not a DVD in the drive.
$ ./findodrive
HP DVD Writer 1070d /dev/sr0 /media/Kota_Kinabalu1 Kota_Kinabalu1
$ ./findodrive
HP DVD Writer 1070d /dev/sr0
Here is a script to list details about all removable USB storage drives that are currently installed on your system.
#!/bin/ksh93
#
# list attached USB storage devices
#

for udi in $(/usr/bin/hal-find-by-capability --capability storage)
do
device=$(hal-get-property --udi $udi --key block.device)
vendor=$(hal-get-property --udi $udi --key storage.vendor)
model=$(hal-get-property --udi $udi --key storage.model)
if [[ $(hal-get-property --udi $udi --key storage.bus) = "usb" ]]
then
parent_udi=$(hal-find-by-property --key block.storage_device --string $udi)
mount=$(hal-get-property --udi $parent_udi --key volume.mount_point)
label=$(hal-get-property --udi $parent_udi --key volume.label)
media_size=$(hal-get-property --udi $udi --key storage.removable.media_size)
size=$(( ceil(media_size/(1000*1000*1000)) ))
printf "$vendor $model $device $mount $label "${size}GB" \n"
fi
done
And here is the output when two USB thumb drives are present.
./listusb   
Kingston DataTraveler 2.0 /dev/sdd /media/KINGSTON KINGSTON 1GB
Kingston DataTraveler 2.0 /dev/sdc /media/USB-4GB USB-4GB 4GB
$
Well that is about all for now.  You should have gained sufficient information from this post to go away and experiment on your own system. 

Remember, however, that the HAL specification and it's implementation in GNU/Linux distributions is still somewhat in a state of flux.  The above examples worked on Fedora 10 as of the date of this post.  If an example does not work on your system, I recommend that you first check the output from the lshal utility to see if the device object and associated device properties are instantiated on your system.  You can use udevadm --monitor to see what kernel events are being pushed out to user space.  HAL monitors these events.  You can also use lshal --monitor to see what events HAL emits to user space.
 

1 comments:

Finnbarr P. Murphy said...

Updated 3/16/09 to include example on how to list attached USB storage devices

Post a Comment