Note: HAL is deprecated. This task now falls to ACPID, which handles it like this.
I wanted to re-purpose my power button in GNU/Linux. This is how I did it.
From about 2006 through 2010, HAL managed the routing of ACPI hardware events. HAL decoupled system events (like pressing the power button) from actions (like calling /sbin/shutdown -h). Here's how it worked:
The kernel delivers the fact that the power button has been pressed to userland in at least two ways. HAL caught both of them. The first path is that the kernel writes to /proc/acpi/event. No history is available here -- reads of this file block until an event happens. So, to receive these events, a daemon has to hold this file open. hald-addon-acpi did this:
# lsof -n | grep /proc/acpi/event hald-addo 12735 haldaemon 4r REG 0,3 0 4026531935 /proc/acpi/event # ps -opid,command -p 12735 PID COMMAND 12735 hald-addon-acpi: listening on acpi kernel interface /proc/acpi/event
The second way power button events are delivered is via the /dev/input/event* devices. HAL's configuration for the power button is visible in the output of hal-device:
... 58: udi = '/org/freedesktop/Hal/devices/computer_logicaldev_input' linux.sysfs_path = '/sys/devices/LNXSYSTM:00/LNXPWRBN:00/input/input1/event1' (string) info.parent = '/org/freedesktop/Hal/devices/computer' (string) info.subsystem = 'input' (string) info.product = 'Power Button' (string) info.udi = '/org/freedesktop/Hal/devices/computer_logicaldev_input' (string) linux.device_file = '/dev/input/event1' (string) info.category = 'input' (string) info.capabilities = { 'input', 'button' } (string list) input.device = '/dev/input/event1' (string) input.product = 'Power Button' (string) button.has_state = false (bool) button.type = 'power' (string) linux.subsystem = 'input' (string) linux.hotplug_type = 2 (0x2) (int) info.addons.singleton = { 'hald-addon-input' } (string list) ...
So the power button on this machine is /dev/input/event1. HAL has a daemon listening on this device also:
# lsof -n | grep /dev/input/event1 hald-addo 12720 root 4r CHR 13,65 0t0 5347 /dev/input/event1 # ps -opid,command -p 12720 PID COMMAND 12720 hald-addon-input: Listening on /dev/input/event1
It is this second input that generates a message on the System DBUS announcing the event. You can use dbus-monitor --system to see these messages:
signal sender=:1.0 -> dest=(null destination) path=/org/freedesktop/Hal/devices/computer_logicaldev_input; interface=org.freedesktop.Hal.Device; member=Condition string "ButtonPressed" string "power"
At this point, HAL thinks it's done. It has intercepted the system event and turned it into a DBUS message. HAL has not called /sbin/shutdown -h, and it won't. The decision of whether or not to do this falls to some listener on the System DBUS.
If nothing is listening, no action will result. On headless systems that use HAL but do not host a Gnome, KDE, etc. session, the power button is likely to do nothing at all. (And here I thought I would have to fight with HAL to override some default action. Haha.)
Normally, in Gnome, KDE, etc., the HAL client would receive the power button message, possibly confirm shutdown with the user in the GUI, and then call the org.freedesktop.Hal.Device.SystemPowerManagement Shutdown() method via DBUS:
/usr/share/hal/fdi/policy/10osvendor/10-power-mgmt-policy.fdi provides a Shutdown method:
<deviceinfo version="0.2"> ... <device> <match key="info.udi" string="/org/freedesktop/Hal/devices/computer"> <append key="info.interfaces" type="strlist">org.freedesktop.Hal.Device.SystemPowerManagement</append> ... <append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_names" type="strlist">Shutdown</append> <append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_signatures" type="strlist"></append> <append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_argnames" type="strlist"></append> <append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_execpaths" type="strlist">hal-system-power-shutdown</append>
This policy is visible in the output of hal-device as part the computer device:
8: udi = '/org/freedesktop/Hal/devices/computer' power_management.quirk.vga_mode_3 = true (bool) info.addons = { 'hald-addon-cpufreq', 'hald-addon-acpi' } (string list) org.freedesktop.Hal.Device.SystemPowerManagement.method_names = { 'Suspend', 'SuspendHybrid', 'Hibernate', 'Shutdown', 'Reboot', 'SetPowerSave' } (string list) org.freedesktop.Hal.Device.SystemPowerManagement.method_signatures = { 'i', 'i', '', '','', 'b' } (string list) org.freedesktop.Hal.Device.SystemPowerManagement.method_argnames = { 'num_seconds_to_sleep', 'num_seconds_to_sleep', '', '', '', 'enable_power_save' } (string list) org.freedesktop.Hal.Device.SystemPowerManagement.method_execpaths = { 'hal-system-power-suspend', 'hal-system-power-suspend-hybrid', 'hal-system-power-hibernate', 'hal-system-power-shutdown', 'hal-system-power-reboot', 'hal-system-power-set-power-save' } (string list)
hal-system-power-shutdown is a shell script in /usr/lib/hal/scripts/ that checks permissions and invokes hal_exec_backend:
#!/bin/sh . hal-functions if [ "$CK_NUM_SESSIONS" -gt "1" ] ; then hal_check_priv org.freedesktop.hal.power-management.shutdown-multiple-sessions else hal_check_priv org.freedesktop.hal.power-management.shutdown fi hal_exec_backend
hal_exec_backend is defined in /usr/lib/hal/scripts/hal-functions. It sticks 'linux' in a few places and calls /usr/lib/hal/scripts/linux/hal-system-power-shutdown-linux:
hal_exec_backend() { local PROGRAM PROGRAM=$(basename $0) if [ -n "$HALD_UNAME_S" -a -x ./$HALD_UNAME_S/$PROGRAM-$HALD_UNAME_S ]; then exec ./$HALD_UNAME_S/$PROGRAM-$HALD_UNAME_S $@ else echo "org.freedesktop.Hal.Device.UnknownError" >&2 echo "No back-end for your operating system" >&2 exit 1 fi }
/usr/lib/hal/scripts/linux/hal-system-power-shutdown-linux finally does the actual /sbin/shutdown -h. Whew!
#!/bin/sh unsupported() { echo "org.freedesktop.Hal.Device.SystemPowerManagement.NotSupported" >&2 echo "No shutdown command found" >&2 exit 1 } #Try for common tools if [ -x "/sbin/shutdown" ] ; then /sbin/shutdown -h now exit $? elif [ -x "/usr/sbin/shutdown" ] ; then /usr/sbin/shutdown -h now exit $? else unsupported fi
So, to receive that power button event, we have to listen on the System DBUS. This means connecting to /var/run/dbus/system_bus_socket and speaking the DBUS protocol. The path of least resistance from here is to pull in a DBUS library. Python's DBUS binding is relatively straightforward. Here's a simple script to listen for power button events and perform some action on receiving them (here, as an example, printing a message and launching a process).
#!/usr/bin/python import dbus from dbus.mainloop.glib import DBusGMainLoop import gobject import subprocess def action(*args): if len(args) == 2 and args[0] == "ButtonPressed" and args[1] == "power": # The action to perform when the power button is pressed print "Power button pressed!" subprocess.call(["beep", "-f104", "-l40", "-r2"]); # Initialize the event loop DBusGMainLoop(set_as_default=True) # Connect to the System DBUS system_bus = dbus.SystemBus() # Declare an interest in DBUS signals system_bus.add_signal_receiver(action) # Wait for events gobject.MainLoop().run()
If you are running a Gnome, KDE, etc. session, you may need to prevent its DBUS listener from shutting down the machine. In Gnome, this configuration is in System :: Preferences :: Power Management :: General tab :: Actions :: When the power button is pressed. If you need to change this setting to 'Do Nothing' programmatically, gconftool is your friend:
gconftool-2 -s '/apps/gnome-power-manager/buttons/power' --type string nothing