Translate

Image of Beginning Google Maps API 3
Image of Operating System Concepts
Image of XSLT 2.0 and XPath 2.0 Programmer's Reference (Programmer to Programmer)
Image of RHCE Red Hat Certified Engineer Linux Study Guide (Exam RH302) (Certification Press)

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-polling disable polling on drives with removable media
hal-find-by-capability find device objects by capability matching
hal-find-by-property find device objects by property matching
hal-get-property get a property from a device object
hal-is-caller-locked-out   determine if caller is locked out
hal-is-caller-privileged determine if caller is privileged
hal-lock lock an interface
hal-set-property set a property on a device object
lshal list 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.
 

UPDATE 6/30/2009 HAL is going to be phased out – at least from the Fedora/Redhat/CenOS stream.

Date: Tue, 30 Jun 2009 16:06:44 +0200
From: Kay Sievers
Subject: HAL is in maintenance mode
To: hal
Message-ID: <1246370804.4911.172.camel@yio.site>
Content-Type: text/plain

Hey,
to clarify some confusion about HAL:

The goal is to get entirely rid of HAL, at least on Linux systems.
We will no longer accept any new features, only bug fixes,
or hal-info entries and fixes. There will be no future release with
major new features, or support for new device types.

If distros ship bug fixes for the current HAL in their packages,
please send them now, so we can integrate them and include them in
a new maintenance release. If distros already added new features
in their packages, please consider to dropping them, or let us
find out how to possibly integrate them now, if its simple enough.

The development has moved and focuses on udev and the DeviceKit-*
projects, please integrate new features there.

There is a separation between udev and the higher layer projects:
Udev will exist on almost every Linux system, and low-level users
can enumerate all current devices and listen to events with libudev.
Applications can expect udev to always be active on the system.

For higher-level projects there are specific susbsystem services,
like DeviceKit-disks, DeviceKit-power, NetworkManager, PulseAudio,
… They all connect to the system device information with libudev,
but provide their higher-level interfaces.

Unlike HAL, applications can not always expect that all these
services are available. Customized systems or large servers may
not be able to run them, or may not even want to install them for
whatever reasons. Usual desktop installations will likely pull-in
most of these services as dependencies though.

Thanks,
Kay

1 comment to Scripting HAL