hal: Branch 'master' - 6 commits

David Zeuthen david at kemper.freedesktop.org
Mon Jul 2 16:10:27 PDT 2007


 doc/spec/hal-spec-interfaces.xml          |  100 ++++
 doc/spec/hal-spec-properties.xml          |   75 +++
 fdi/policy/10osvendor/10-input-policy.fdi |    8 
 hald-runner/main.c                        |   51 ++
 hald-runner/runner.c                      |   37 +
 hald-runner/runner.h                      |    1 
 hald/hald.c                               |   26 +
 hald/hald_dbus.c                          |  327 ++++++++++++++++
 hald/hald_dbus.h                          |    3 
 hald/hald_runner.c                        |   78 ++-
 hald/hald_runner.h                        |    6 
 hald/linux/addons/Makefile.am             |    2 
 hald/linux/addons/addon-input.c           |  295 +++++++++++---
 libhal/Makefile.am                        |    4 
 libhal/libhal.c                           |  576 ++++++++++++++++++++--------
 libhal/libhal.h                           |   64 ++-
 libhal/uthash.h                           |  605 ++++++++++++++++++++++++++++++
 17 files changed, 1977 insertions(+), 281 deletions(-)

New commits:
diff-tree 3a5e6376eb23fe3f44b2a10c40549cae29c2200d (from fe2c65da50d93f8242fddfeb164337fee4be1873)
Author: Rob Taylor <rob.taylor at codethink.co.uk>
Date:   Fri Jun 22 13:21:18 2007 +0100

    [PATCH] document singleton addons
    
    Adds documentation to the spec for info.addons.singleton and the org.freedesktop.Hal.SingletonAddon interface.

diff --git a/doc/spec/hal-spec-interfaces.xml b/doc/spec/hal-spec-interfaces.xml
index 2511011..4e5050f 100644
--- a/doc/spec/hal-spec-interfaces.xml
+++ b/doc/spec/hal-spec-interfaces.xml
@@ -142,7 +142,18 @@ $ dbus-send --system --print-reply --des
               <xref linkend="locking"/> for details.
             </entry>
           </row>
-
+          <row>
+            <entry>SingletonAddonIsReady</entry>
+            <entry></entry>
+            <entry>String command_line</entry>
+            <entry>PermissionDenied, SyntaxError</entry>
+            <entry>
+              Called by singleton addons to signal that they are
+              ready to handle devices.  A singleton addon should
+              implement the <link linkend="interface-singleton-addon">
+                org.freedesktop.Hal.Singleton</link> interface.
+            </entry>
+          </row>
         </tbody>
       </tgroup>
     </informaltable>
@@ -643,7 +654,6 @@ $ dbus-send --system --print-reply --des
     </informaltable>
   </sect1>
 
-
   <sect1 id="interface-device-systempower">
     <title>org.freedesktop.Hal.Device.SystemPowerManagement interface</title>
     <para>
@@ -1281,6 +1291,92 @@ $ dbus-send --system --print-reply --des
 
   </sect1>
 
+  <sect1 id="interface-singleton-addon">
+    <title><literal>org.freedesktop.Hal.SingletonAddon</literal> interface</title>
+    <para>
+      This interface is provided by singleton addons to allow the Manager to
+      request handling of new devices and removal of old ones.
+
+      This differs from other HAL interface definitions in that it
+      is provided by addon processes, rather than the HAL daemon itself.
+
+      It should be exported on the path
+      <literal>/org/freedesktop/Hal/Manager</literal>.
+    </para>
+
+    <informaltable>
+      <tgroup cols="2">
+        <thead>
+          <row>
+            <entry>Method</entry>
+            <entry>Returns</entry>
+            <entry>Parameters</entry>
+            <entry>Throws</entry>
+            <entry>Description</entry>
+          </row>
+        </thead>
+        <tbody>
+          <row>
+            <entry>
+              <literal>DeviceAdded</literal>
+            </entry>
+            <entry>
+              <para><literal>String udi</literal></para>
+              <para><literal>Dict(String,Variant) property_set</literal></para>
+            </entry>
+            <entry></entry>
+            <entry></entry>
+            <entry>
+              <para>
+                Called by the HAL Manager when a device is added that has
+                this singleton listed in
+                <link linkend="device-properties-info-singleton-addons">
+                  <literal>info.addons.singleton</literal>
+                </link>
+              </para>
+              <para>
+                An addon implementing this function should start handling the
+                device before returning, and keep track that is is handling this
+                udi.
+              </para>
+            </entry>
+          </row>
+          <row>
+            <entry>
+              <literal>DeviceRemoved</literal>
+            </entry>
+            <entry>
+              <para><literal>String udi</literal></para>
+              <para><literal>Dict(String,Variant) property_set</literal></para>
+            </entry>
+            <entry></entry>
+            <entry></entry>
+            <entry>
+              <para>
+                Called by the HAL Manager when a device is added that has
+                this singleton listed in
+                <link linkend="device-properties-info-singleton-addons">
+                  <literal>info.addons.singleton</literal>
+                </link>
+              </para>
+              <para>
+                The implementer of this function should keep track of
+                which devices it is still handling and exit when no longer
+                handling any devices.
+              </para>
+            </entry>
+          </row>
+
+
+        </tbody>
+      </tgroup>
+    </informaltable>
+
+    <para>
+      This interface does not export any methods.
+    </para>
+  </sect1>
+
 <!--
   <sect1 id="interface-device-foo">
     <title>org.freedesktop.Hal.Device.Foo interface</title>
diff --git a/doc/spec/hal-spec-properties.xml b/doc/spec/hal-spec-properties.xml
index 730f826..c94e2a8 100644
--- a/doc/spec/hal-spec-properties.xml
+++ b/doc/spec/hal-spec-properties.xml
@@ -434,7 +434,80 @@ CK_SESSION_UID_Session3=501
       </informaltable>
       
     </sect2>
-    <sect2 id="device-properties-info-method-calls">
+    <sect2 id="device-properties-info-singleton-addons">
+      <title>Singleton Addons</title>
+      
+      <para>
+        Singleton Addons are programs that are started by HAL to
+        handle a set of devices. They are identified by the command line
+        used to start them. They MUST implement the
+	<link linkend="interface-singleton-addon">
+	  <literal>org.freedesktop.Hal.SingletonAddon</literal>
+	</link>
+	interface.  on the path
+	<literal>/org/freedesktop/Hal/Singleton</literal> path on
+        the direct connection to the HAL daemon.
+      </para>
+      <para>
+        When a device is added with an <literal>info.addons.singleton</literal>
+        string list key, the elements of that key are used as the command line
+        to start the singleton if the singleton is not already running.
+        Once the singleton has called <literal>SingletonAddonIsReady</literal> on
+	<link linkend="interface-manager">
+	  <literal>org.freedesktop.Hal.Manager</literal>
+	</link> interface, it will receive
+	<literal>DeviceAdded</literal> calls on its
+	<link linkend="interface-singleton-addon">
+	  <literal>org.freedesktop.Hal.SingletonAddon</literal>
+	</link>
+        interface for all devices that have
+        its commandline in <literal>info.addons.singletona</literal>.
+      </para>
+      <para>
+        If a device is added and the singleton specified in
+        <literal>info.addons.singleton</literal> is already running, the
+        singleton will recieve <literal>DeviceAdded</literal> on its
+	<link linkend="interface-singleton-addon">
+	  <literal>org.freedesktop.Hal.SingletonAddon</literal>
+	</link>
+	interface for that new device.  </para>
+      <para>
+        When a device is removed that is being handled by a singleton, the
+	singleton will recieve <literal>DeviceRemoved</literal> on
+	<link linkend="interface-singleton-addon">
+	  <literal>org.freedesktop.Hal.SingletonAddon</literal>
+	</link>.
+	When it is no longer handling any more devices it should exit cleanly.
+      </para>
+      
+      <informaltable>
+        <tgroup cols="2">
+          <thead>
+            <row>
+              <entry>Key (type)</entry>
+              <entry>Values</entry>
+              <entry>Mandatory</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>
+                <literal>info.addons.singleton</literal> (strlist)
+              </entry>
+              <entry></entry>
+              <entry>No</entry>
+              <entry>
+                A list of commandlines for singleton addons which should
+                service this device.
+              </entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </informaltable>
+      
+    </sect2>
+     <sect2 id="device-properties-info-method-calls">
       <title>Method calls</title>
       
       <para>
diff-tree fe2c65da50d93f8242fddfeb164337fee4be1873 (from 9b91611b5803faaf7578efb25db8951790790f3e)
Author: Rob Taylor <rob.taylor at codethink.co.uk>
Date:   Fri Jun 22 14:47:34 2007 +0100

    [PATCH] convert linux addon-input into a singleton addon
    
    Converts hald-addon-input to a singleton addon, making use of the new additions
    to libhal api.  This uses the glib mainloop for listening to input events and
    the dbus connection. Adds dependancy on glib and dbus-glib.

diff --git a/fdi/policy/10osvendor/10-input-policy.fdi b/fdi/policy/10osvendor/10-input-policy.fdi
index be4c235..8aecb81 100644
--- a/fdi/policy/10osvendor/10-input-policy.fdi
+++ b/fdi/policy/10osvendor/10-input-policy.fdi
@@ -5,13 +5,13 @@
   <device>
     <match key="info.capabilities" contains="input">
       <match key="info.capabilities" contains="button">
-	<match key="info.addons" contains_not="hald-addon-input">
-	  <append key="info.addons" type="strlist">hald-addon-input</append>
+	<match key="info.addons.singleton" contains_not="hald-addon-input">
+	  <append key="info.addons.singleton" type="strlist">hald-addon-input</append>
         </match>
       </match>
       <match key="info.capabilities" contains="input.keys">
-	<match key="info.addons" contains_not="hald-addon-input">
-	  <append key="info.addons" type="strlist">hald-addon-input</append>
+	<match key="info.addons.singleton" contains_not="hald-addon-input">
+	  <append key="info.addons.singleton" type="strlist">hald-addon-input</append>
         </match>
         <match key="info.capabilities" contains_not="button">
 	  <append key="info.capabilities" type="strlist">button</append>
diff --git a/hald/linux/addons/Makefile.am b/hald/linux/addons/Makefile.am
index 6a6a268..8546f02 100644
--- a/hald/linux/addons/Makefile.am
+++ b/hald/linux/addons/Makefile.am
@@ -79,7 +79,7 @@ hald_addon_hid_ups_SOURCES = addon-hid-u
 hald_addon_hid_ups_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@
 
 hald_addon_input_SOURCES = addon-input.c ../../logger.c ../../util_helper.c
-hald_addon_input_LDADD = $(top_builddir)/libhal/libhal.la
+hald_addon_input_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@
 
 hald_addon_pmu_SOURCES = addon-pmu.c ../../logger.c ../../util_helper.c
 hald_addon_pmu_LDADD = $(top_builddir)/libhal/libhal.la
diff --git a/hald/linux/addons/addon-input.c b/hald/linux/addons/addon-input.c
index 6f3167f..2ba923b 100644
--- a/hald/linux/addons/addon-input.c
+++ b/hald/linux/addons/addon-input.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2005 David Zeuthen, <david at fubar.dk>
  * Copyright (C) 2005 Ryan Lortie <desrt at desrt.ca>
  * Copyright (C) 2006 Matthew Garrett <mjg59 at srcf.ucam.org>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,16 +33,19 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include <linux/input.h>
+#include <glib/gmain.h>
+#include <glib/gprintf.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
 #include "libhal/libhal.h"
 
 #include "../../logger.h"
 #include "../../util_helper.h"
 
-static char *udi;
-static int button_state = FALSE;
-static int button_has_state = FALSE;
 
 static char *key_name[KEY_MAX + 1] = {
 	[0 ... KEY_MAX] = NULL,
@@ -179,23 +183,58 @@ static char *key_name[KEY_MAX + 1] = {
 #define LONG(x) ((x)/BITS_PER_LONG)
 #define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
 
-static void
-main_loop (LibHalContext *ctx, FILE* eventfp)
+typedef struct _InputData InputData;
+struct _InputData
 {
-	DBusError error;
 	struct input_event event;
+	gsize offset;
+	gboolean button_has_state;
+	gboolean button_state;
+	char udi[1];			/*variable size*/
+};
 
+static LibHalContext *ctx = NULL;
+static GMainLoop *gmain = NULL;
+static GHashTable *inputs = NULL;
+static GList *devices = NULL;
 
-	while (fread (&event, sizeof(event), 1, eventfp)) {
+static gboolean
+event_io (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+	InputData *input_data = (InputData*) data;
+	DBusError error;
+	GError *gerror = NULL;
+	gsize read_bytes;
+
+	if (condition & (G_IO_HUP | G_IO_ERR))
+		return FALSE;
+
+	/** tbh, we can probably assume every time we read we have a whole
+	 * event availiable, but hey..*/
+	while (g_io_channel_read_chars (channel,
+			((gchar*)&input_data->event) + input_data->offset,
+			sizeof(struct input_event) - input_data->offset,
+			&read_bytes, &gerror) == G_IO_STATUS_NORMAL) {
+
+		if (input_data->offset + read_bytes < sizeof (struct input_event)) {
+			input_data->offset = input_data->offset + read_bytes;
+			HAL_DEBUG (("incomplete read"));
+			return TRUE;
+		} else {
+			input_data->offset = 0;
+		}
 
-		if (button_has_state && event.type == EV_SW) {
+		if (input_data->button_has_state &&
+		    input_data->event.type == EV_SW) {
 			char *name = NULL;
 
-			HAL_INFO (("%s: event.value=%d ; event.code=%d (0x%02x)", 
-				   udi, event.value, event.code, event.code));
+			HAL_INFO (("%s: event.value=%d ; event.code=%d (0x%02x)",
+				   input_data->udi, input_data->event.value,
+				   input_data->event.code,
+				   input_data->event.code));
 
 
-			switch (event.code) {
+			switch (input_data->event.code) {
 			case SW_LID:
 				name = "lid";
 				break;
@@ -225,100 +264,232 @@ main_loop (LibHalContext *ctx, FILE* eve
 				 * 19:08:26.960 [I] event.value=0 ; event.code=0 (0x00)
 				 */
 
-				if (ioctl (fileno (eventfp), EVIOCGSW(sizeof (bitmask)), bitmask) < 0) {
+				if (ioctl (g_io_channel_unix_get_fd(channel), EVIOCGSW(sizeof (bitmask)), bitmask) < 0) {
 					HAL_DEBUG (("ioctl EVIOCGSW failed"));
 				} else {
-					int new_state = test_bit (event.code, bitmask);
-					if (new_state != button_state) {
-						button_state = new_state;
-						
+					int new_state = test_bit (input_data->event.code, bitmask);
+					if (new_state != input_data->button_state) {
+						input_data->button_state = new_state;
+
 						dbus_error_init (&error);
-						libhal_device_set_property_bool (ctx, udi, "button.state.value", 
-										 button_state, &error);
-						
+						libhal_device_set_property_bool (ctx, input_data->udi, "button.state.value",
+										 input_data->button_state, &error);
+
 						dbus_error_init (&error);
-						libhal_device_emit_condition (ctx, udi, 
+						libhal_device_emit_condition (ctx, input_data->udi,
 									      "ButtonPressed",
 									      name,
 									      &error);
+						dbus_error_free (&error);
 					}
 				}
 			}
-		} else if (event.type == EV_KEY && key_name[event.code] != NULL && event.value == 1) {
+		} else if (input_data->event.type == EV_KEY && key_name[input_data->event.code] != NULL && input_data->event.value == 1) {
 			dbus_error_init (&error);
-			libhal_device_emit_condition (ctx, udi, 
+			libhal_device_emit_condition (ctx, input_data->udi,
 						      "ButtonPressed",
-						      key_name[event.code],
+						      key_name[input_data->event.code],
 						      &error);
+			dbus_error_free (&error);
 		}
 	}
-	
-	dbus_error_free (&error);
+
+	return TRUE;
+}
+
+static void
+destroy_data (InputData *data)
+{
+	HAL_DEBUG (("Removing GIOChannel for'%s'", data->udi));
+
+	/* Null out the GIOChannel in the hash table, but
+	 * leave the key in for DeviceRemoved
+	 */
+	g_hash_table_replace (inputs, g_strdup(data->udi), NULL);
+
+	g_free (data);
+}
+
+
+static void
+update_proc_title ()
+{
+	GList *lp;
+	gchar *new_command_line, *p;
+	gint len = 0;
+
+	for (lp = devices; lp; lp = lp->next)
+		len = len + strlen (lp->data) + 1;
+
+	len += strlen ("hald-addon-input: Listening on");
+
+	new_command_line = g_malloc (len + 1);
+	p = new_command_line;
+	p += g_sprintf (new_command_line, "hald-addon-input: Listening on");
+
+	for (lp = g_list_last(devices); lp; lp = g_list_previous(lp))
+	{
+		p += g_sprintf (p, " %s", (gchar*) lp->data);
+	}
+
+	hal_set_proc_title (new_command_line);
+	g_free (new_command_line);
+}
+
+static void
+add_device (LibHalContext *ctx,
+	    const char *udi,
+	    const LibHalPropertySet *properties)
+{
+	int eventfp;
+	GIOChannel *channel;
+	InputData *data;
+	int len = strlen (udi);
+	const char* device_file;
+
+	data = (InputData*) g_malloc (sizeof (InputData) + len);
+
+	memcpy (&(data->udi), udi, len+1);
+
+	if ((device_file = libhal_ps_get_string (properties, "input.device")) == NULL) {
+		HAL_ERROR(("%s has no property input.device", udi));
+		return;
+	}
+
+	/* button_has_state will be false if the key isn't available*/
+	data->button_has_state = libhal_ps_get_bool (properties, "button.has_state");
+	if (data->button_has_state)
+		data->button_state = libhal_ps_get_bool (properties, "button.state.value");
+
+	data->offset = 0;
+	eventfp = open(device_file, O_RDONLY | O_NONBLOCK);
+	if (!eventfp) {
+		HAL_ERROR(("Unable to open %s for reading", device_file));
+		return;
+	}
+
+
+	HAL_DEBUG (("%s: Listening on %s", udi, device_file));
+
+	devices = g_list_prepend (devices, g_strdup (device_file));
+	update_proc_title ();
+
+	channel = g_io_channel_unix_new (eventfp);
+	g_io_channel_set_encoding (channel, NULL, NULL);
+
+	g_hash_table_insert (inputs, g_strdup(udi), channel);
+	g_io_add_watch_full (channel,
+			     G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP,
+			     event_io, data, (GDestroyNotify) destroy_data);
+}
+
+
+static void
+remove_device (LibHalContext *ctx,
+	    const char *udi,
+	    const LibHalPropertySet *properties)
+{
+
+	GIOChannel *channel, **p_channel = &channel;
+	const gchar *device_file;
+	GList *lp;
+	gboolean handling_udi;
+
+	HAL_DEBUG (("Removing channel for '%s'", udi));
+
+	handling_udi = g_hash_table_lookup_extended (inputs, udi, NULL, (gpointer *)p_channel);
+
+	if (!handling_udi) {
+		HAL_ERROR(("DeviceRemove called for unknown device: '%s'.", udi));
+		return;
+	}
+
+	if (channel)
+		g_io_channel_unref (channel);
+
+	g_hash_table_remove (inputs, udi);
+
+	if ((device_file = libhal_ps_get_string (properties, "input.device")) == NULL) {
+		HAL_ERROR(("%s has no property input.device", udi));
+		return;
+	}
+
+	lp = g_list_find_custom (devices, device_file, (GCompareFunc) strcmp);
+	if (lp) {
+		devices = g_list_remove_link (devices, lp);
+		g_free (lp->data);
+		g_list_free_1 (lp);
+		update_proc_title ();
+	}
+
+	if (g_hash_table_size (inputs) == 0) {
+		HAL_INFO(("no more devices, exiting"));
+		g_main_loop_quit (gmain);
+	}
+
+
 }
 
 int
 main (int argc, char **argv)
 {
-	LibHalContext *ctx = NULL;
+	DBusConnection *dbus_connection;
 	DBusError error;
-	char *device_file;
-	FILE *eventfp;
-	char *s;
+	const char *commandline;
 
 	hal_set_proc_title_init (argc, argv);
 
 	/* setup_logger (); */
 
 	dbus_error_init (&error);
-
-	if ((udi = getenv ("UDI")) == NULL)
+	if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
+		HAL_WARNING (("Unable to init libhal context"));
 		goto out;
-	
-	if ((device_file = getenv ("HAL_PROP_INPUT_DEVICE")) == NULL)
+	}
+
+	if ((dbus_connection = libhal_ctx_get_dbus_connection(ctx)) == NULL) {
+		HAL_WARNING (("Cannot get DBus connection"));
 		goto out;
+	}
 
-	s = getenv ("HAL_PROP_BUTTON_HAS_STATE");
-	if (s != NULL && strcmp (s, "true") == 0) {
-		button_has_state = TRUE;
-		if ((s = getenv ("HAL_PROP_BUTTON_STATE_VALUE")) == NULL)
-			goto out;
-		button_state = (strcmp (s, "true") == 0);
+	if ((commandline = getenv ("SINGLETON_COMMAND_LINE")) == NULL) {
+		HAL_WARNING (("SINGLETON_COMMAND_LINE not set"));
+		goto out;
 	}
 
-	if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
-                goto out;
+	libhal_ctx_set_singleton_device_added (ctx, add_device);
+	libhal_ctx_set_singleton_device_removed (ctx, remove_device);
+
+	dbus_connection_setup_with_g_main (dbus_connection, NULL);
+	dbus_connection_set_exit_on_disconnect (dbus_connection, 0);
 
 	dbus_error_init (&error);
-	if (!libhal_device_addon_is_ready (ctx, udi, &error)) {
+
+	if (!libhal_device_singleton_addon_is_ready (ctx, commandline, &error)) {
 		goto out;
 	}
 
-	eventfp = fopen(device_file, "r");	
-
-	if (!eventfp)
-		goto out;
+/*
+ * We should do real privilage dropping here, or we can do drop_privialages
+ * if haldaemon user can read input.
+ * drop_privileges (0);
+*/
 
-	drop_privileges (0);
+	inputs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
-	hal_set_proc_title ("hald-addon-input: listening on %s", device_file);
+	gmain = g_main_loop_new (NULL, FALSE);
+	g_main_loop_run (gmain);
 
-	while (1)
-	{
-		main_loop (ctx, eventfp);
-		
-		/* If main_loop exits sleep for 5s and try to reconnect (again). */
-		sleep (5);
-	}
-	
 	return 0;
 
- out:
+out:
+	HAL_DEBUG (("An error occured, exiting cleanly"));
 	if (ctx != NULL) {
-                dbus_error_init (&error);
-                libhal_ctx_shutdown (ctx, &error);
-                libhal_ctx_free (ctx);
-        }
-	
+		dbus_error_init (&error);
+		libhal_ctx_shutdown (ctx, &error);
+		libhal_ctx_free (ctx);
+	}
+
 	return 0;
 }
 
diff-tree 9b91611b5803faaf7578efb25db8951790790f3e (from e9717094756042e4cb69000bea8ed14d8bbd2645)
Author: Rob Taylor <rob.taylor at codethink.co.uk>
Date:   Wed Jun 6 18:35:02 2007 +0100

    [PATCH] convert libhal to use a hashtable for property sets
    
    Uses uthash (http://uthash.sourceforge.net/) as a hashtable implementation
    for LibHalPropertySet. Slight changes have been made to uthash.h to remove float usage and clean up compiler warnings.

diff --git a/libhal/Makefile.am b/libhal/Makefile.am
index bd8bbb8..12ae917 100644
--- a/libhal/Makefile.am
+++ b/libhal/Makefile.am
@@ -13,7 +13,9 @@ libhalinclude_HEADERS =                 
 	libhal.h
 
 libhal_la_SOURCES =                                       \
-	libhal.c                  libhal.h
+	libhal.c \
+	libhal.h \
+	uthash.h
 
 
 if GCOV
diff --git a/libhal/libhal.c b/libhal/libhal.c
index 1f531b3..1763853 100644
--- a/libhal/libhal.c
+++ b/libhal/libhal.c
@@ -34,6 +34,7 @@
 #include <string.h>
 #include <dbus/dbus.h>
 
+#include "uthash.h"
 #include "libhal.h"
 
 #ifdef ENABLE_NLS
@@ -184,10 +185,7 @@ libhal_free_string (char *str)
  * libhal_property_set_*() family of functions to access it.
  */
 struct LibHalPropertySet_s {
-	unsigned int num_properties; /**< Number of properties in set */
-	LibHalProperty *properties_head;
-				     /**< Pointer to first property or NULL
-				      *	  if there are no properties */
+	LibHalProperty *properties;
 };
 
 /**
@@ -213,8 +211,7 @@ struct LibHalProperty_s {
 		char **strlist_value; /**< List of UTF-8 zero-terminated strings */
 	} v;
 
-	LibHalProperty *next;	     /**< Next property or NULL if this is 
-				      *	  the last */
+	UT_hash_handle hh;		/*makes this hashable*/
 };
 
 /**
@@ -411,8 +408,7 @@ get_property_set (DBusMessageIter *iter)
     }
 */
 
-	result->properties_head = NULL;
-	result->num_properties = 0;
+	result->properties = NULL;
 
 	if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_ARRAY  &&
 	    dbus_message_iter_get_element_type (iter) != DBUS_TYPE_DICT_ENTRY) {
@@ -439,16 +435,6 @@ get_property_set (DBusMessageIter *iter)
 		if (p == NULL)
 			goto oom;
 
-		p->next = NULL;
-
-		if (result->num_properties == 0)
-			result->properties_head = p;
-
-		if (p_last != NULL)
-			p_last->next = p;
-
-		p_last = p;
-
 		p->key = strdup (key);
 		if (p->key == NULL)
 			goto oom;
@@ -457,14 +443,13 @@ get_property_set (DBusMessageIter *iter)
 
 		dbus_message_iter_recurse (&dict_entry_iter, &var_iter);
 
-
 		p->type = dbus_message_iter_get_arg_type (&var_iter);
-	
-		result->num_properties++;
 
 		if(!libhal_property_fill_value_from_variant (p, &var_iter))
 			goto oom;
 
+                HASH_ADD_KEYPTR (hh, result->properties, p->key, strlen (p->key), p);
+
 		dbus_message_iter_next (&dict_iter);
 	}
 
@@ -541,6 +526,12 @@ libhal_device_get_all_properties (LibHal
 	return result;
 }
 
+static int
+key_sort (LibHalProperty *a, LibHalProperty *b)
+{
+	return strcmp (a->key, b->key);
+}
+
 /**
  * libhal_property_set_sort:
  * @set: property-set to sort
@@ -550,36 +541,7 @@ libhal_device_get_all_properties (LibHal
 void 
 libhal_property_set_sort (LibHalPropertySet *set)
 {
-	unsigned int i;
-	unsigned int num_elements;
-	LibHalProperty *p;
-	LibHalProperty *q;
-	LibHalProperty **r;
-
-	/* TODO: for the sake of gods; do something smarter than a slow bubble-sort!! */
-
-	num_elements = libhal_property_set_get_num_elems (set);
-	for (i = 0; i < num_elements; i++) {
-		for (p = set->properties_head, r = &(set->properties_head); p != NULL; p = q) {
-			q = p->next;
-
-			if (q == NULL)
-				continue;
-
-			if (strcmp (p->key, q->key) > 0) {
-				/* switch p and q */
-				p->next = q->next;
-				q->next = p;
-				*r = q;
-				
-				r = &(q->next);
-				q = p;
-			} else {
-				/* do nothing */
-				r = &(p->next);
-			}
-		}
-	}
+	HASH_SORT (set->properties, key_sort);
 }
 
 /**
@@ -592,18 +554,14 @@ void
 libhal_free_property_set (LibHalPropertySet * set)
 {
 	LibHalProperty *p;
-	LibHalProperty *q;
 
-	if (set == NULL)
-		return;
-
-	for (p = set->properties_head; p != NULL; p = q) {
+	for (p = set->properties; p != NULL; p=p->hh.next) {
+		HASH_DELETE (hh, set->properties, p);
 		free (p->key);
 		if (p->type == DBUS_TYPE_STRING)
 			free (p->v.str_value);
 		if (p->type == LIBHAL_PROPERTY_TYPE_STRLIST)
 			libhal_free_string_array (p->v.strlist_value);
-		q = p->next;
 		free (p);
 	}
 	free (set);
@@ -627,7 +585,7 @@ libhal_property_set_get_num_elems (LibHa
 		return 0;
 	
 	num_elems = 0;
-	for (p = set->properties_head; p != NULL; p = p->next)
+	for (p = set->properties; p != NULL; p = p->hh.next)
 		num_elems++;
 
 	return num_elems;
@@ -638,11 +596,8 @@ property_set_lookup (const LibHalPropert
 {
 	LibHalProperty *p;
 
-	for (p = set->properties_head; p != NULL; p = p->next) {
-		if (strcmp (p->key, key) == 0)
-			return p;
-	}
-	return NULL;
+	HASH_FIND_STR (set->properties, key, p);
+	return p;
 }
 
 /**
@@ -790,8 +745,8 @@ libhal_psi_init (LibHalPropertySetIterat
 		return;
 
 	iter->set = set;
-	iter->idx = 0;
-	iter->cur_prop = set->properties_head;
+	iter->idx = -1; //deprecated
+	iter->cur_prop = set->properties;
 }
 
 
@@ -806,7 +761,7 @@ libhal_psi_init (LibHalPropertySetIterat
 dbus_bool_t
 libhal_psi_has_more (LibHalPropertySetIterator * iter)
 {
-	return iter->idx < iter->set->num_properties;
+	return (iter->cur_prop->hh.next != NULL);
 }
 
 /**
@@ -818,8 +773,7 @@ libhal_psi_has_more (LibHalPropertySetIt
 void
 libhal_psi_next (LibHalPropertySetIterator * iter)
 {
-	iter->idx++;
-	iter->cur_prop = iter->cur_prop->next;
+	iter->cur_prop = iter->cur_prop->hh.next;
 }
 
 /**
diff --git a/libhal/uthash.h b/libhal/uthash.h
new file mode 100644
index 0000000..fb79ca2
--- /dev/null
+++ b/libhal/uthash.h
@@ -0,0 +1,605 @@
+/*
+Copyright (c) 2003-2006, Troy Hanson     http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+    * Neither the name of the copyright holder nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h> /* memcmp,strlen */
+
+#ifndef UTHASH_H
+#define UTHASH_H 
+
+#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
+#define uthash_bkt_malloc(sz) malloc(sz)  /* malloc fcn for UT_hash_bucket's */
+#define uthash_bkt_free(ptr) free(ptr)    /* free fcn for UT_hash_bucket's   */
+#define uthash_tbl_malloc(sz) malloc(sz)  /* malloc fcn for UT_hash_table    */
+#define uthash_tbl_free(ptr) free(ptr)    /* free fcn for UT_hash_table      */
+
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32  /* initial number of buckets        */
+#define HASH_BKT_CAPACITY_THRESH 10  /* expand when bucket count reaches */
+
+#define HASH_FIND(hh,head,keyptr,keylen_in,out)                          \
+do {                                                                     \
+  out=head;                                                              \
+  if (head) {                                                            \
+     (head)->hh.tbl->key = (char*)(keyptr);                              \
+     (head)->hh.tbl->keylen = keylen_in;                                 \
+     HASH_FCN((head)->hh.tbl->key,(head)->hh.tbl->keylen,                \
+             (head)->hh.tbl->num_buckets,(head)->hh.tbl->bkt,            \
+             (head)->hh.tbl->i, (head)->hh.tbl->j,(head)->hh.tbl->k);    \
+     HASH_FIND_IN_BKT(hh, (head)->hh.tbl->buckets[ (head)->hh.tbl->bkt], \
+             keyptr,keylen_in,out);                                      \
+  }                                                                      \
+} while (0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                        \
+        HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
+ 
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                    \
+do {                                                                     \
+ add->hh.next = NULL;                                                    \
+ add->hh.key = (char*)keyptr;                                            \
+ add->hh.keylen = keylen_in;                                             \
+ add->hh.elmt = add;                                                     \
+ if (!(head)) {                                                          \
+    head = add;                                                          \
+    (head)->hh.prev = NULL;                                              \
+    (head)->hh.tbl = (UT_hash_table*)uthash_tbl_malloc(                  \
+                    sizeof(UT_hash_table));                              \
+    if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }          \
+    (head)->hh.tbl->name = #head;                                        \
+    (head)->hh.tbl->tail = &(add->hh);                                   \
+    (head)->hh.tbl->noexpand = 0;                                        \
+    (head)->hh.tbl->hash_q = 1<<16;                                          \
+    (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;              \
+    (head)->hh.tbl->num_items = 0;                                       \
+    (head)->hh.tbl->hho = ((long)(&add->hh) - (long)(add));              \
+    (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_bkt_malloc(        \
+            HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));     \
+    if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }   \
+    memset((head)->hh.tbl->buckets, 0,                                   \
+            HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));     \
+ } else {                                                                \
+    (head)->hh.tbl->tail->next = add;                                    \
+    add->hh.prev = (head)->hh.tbl->tail->elmt;                           \
+    (head)->hh.tbl->tail = &(add->hh);                                   \
+ }                                                                       \
+ (head)->hh.tbl->num_items++;                                            \
+ add->hh.tbl = (head)->hh.tbl;                                           \
+ (head)->hh.tbl->key = (char*)keyptr;                                    \
+ (head)->hh.tbl->keylen = keylen_in;                                     \
+ HASH_FCN((head)->hh.tbl->key,(head)->hh.tbl->keylen,                    \
+         (head)->hh.tbl->num_buckets,                                    \
+         (head)->hh.tbl->bkt,                                            \
+         (head)->hh.tbl->i, (head)->hh.tbl->j, (head)->hh.tbl->k );      \
+ HASH_ADD_TO_BKT(hh,(head)->hh.tbl->buckets[(head)->hh.tbl->bkt],add);   \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                \
+ HASH_FSCK(head);                                                        \
+} while(0)
+
+#define HASH_DELETE(hh,head,delptr)                                      \
+do {                                                                     \
+    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  { \
+        uthash_bkt_free((head)->hh.tbl->buckets );                       \
+        uthash_tbl_free((head)->hh.tbl);                                 \
+        head = NULL;                                                     \
+    } else {                                                             \
+        if ((delptr) == (head)->hh.tbl->tail->elmt) {                    \
+            (head)->hh.tbl->tail = (void*)(((long)((delptr)->hh.prev)) + \
+                                           (head)->hh.tbl->hho);         \
+        }                                                                \
+        if ((delptr)->hh.prev) {                                         \
+            ((UT_hash_handle*)(((long)((delptr)->hh.prev)) +             \
+                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;     \
+        } else {                                                         \
+            head = (delptr)->hh.next;                                    \
+        }                                                                \
+        if ((delptr)->hh.next) {                                         \
+            ((UT_hash_handle*)(((long)((delptr)->hh.next)) +             \
+                    (head)->hh.tbl->hho))->prev = (delptr)->hh.prev;     \
+        }                                                                \
+        (head)->hh.tbl->key = (char*)((delptr)->hh.key);                 \
+        (head)->hh.tbl->keylen = (delptr)->hh.keylen;                    \
+        HASH_FCN((head)->hh.tbl->key,(head)->hh.tbl->keylen,             \
+                (head)->hh.tbl->num_buckets,(head)->hh.tbl->bkt,         \
+                (head)->hh.tbl->i,(head)->hh.tbl->j,(head)->hh.tbl->k ); \
+        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[(head)->hh.tbl->bkt], \
+                delptr);                                                 \
+        (head)->hh.tbl->num_items--;                                     \
+    }                                                                    \
+    HASH_FSCK(head);                                                     \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                 \
+    HASH_FIND(hh,head,findstr,strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add)                                 \
+    HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
+#define HASH_FIND_INT(head,findint,out)                                 \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                 \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_DEL(head,delptr)                                           \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ * This function misuses fields in UT_hash_table for its bookkeeping variables.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(head)                                                  \
+do {                                                                     \
+    if (head) {                                                          \
+        (head)->hh.tbl->keylen = 0;   /* item counter */                 \
+        for(    (head)->hh.tbl->bkt_i = 0;                               \
+                (head)->hh.tbl->bkt_i < (head)->hh.tbl->num_buckets;     \
+                (head)->hh.tbl->bkt_i++)                                 \
+        {                                                                \
+            (head)->hh.tbl->bkt_ideal = 0; /* bkt item counter */        \
+            (head)->hh.tbl->hh =                                         \
+            (head)->hh.tbl->buckets[(head)->hh.tbl->bkt_i].hh_head;      \
+            (head)->hh.tbl->key = NULL;  /* hh_prev */                   \
+            while ((head)->hh.tbl->hh) {                                 \
+               if ((head)->hh.tbl->key !=                                \
+                   (char*)((head)->hh.tbl->hh->hh_prev)) {               \
+                   HASH_OOPS("invalid hh_prev %x, actual %x\n",          \
+                    (head)->hh.tbl->hh->hh_prev,                         \
+                    (head)->hh.tbl->key );                               \
+               }                                                         \
+               (head)->hh.tbl->bkt_ideal++;                              \
+               (head)->hh.tbl->key = (char*)((head)->hh.tbl->hh);        \
+               (head)->hh.tbl->hh = (head)->hh.tbl->hh->hh_next;         \
+            }                                                            \
+            (head)->hh.tbl->keylen +=  (head)->hh.tbl->bkt_ideal;        \
+            if ((head)->hh.tbl->buckets[(head)->hh.tbl->bkt_i].count     \
+               !=  (head)->hh.tbl->bkt_ideal) {                          \
+               HASH_OOPS("invalid bucket count %d, actual %d\n",         \
+                (head)->hh.tbl->buckets[(head)->hh.tbl->bkt_i].count,    \
+                (head)->hh.tbl->bkt_ideal);                              \
+            }                                                            \
+        }                                                                \
+        if ((head)->hh.tbl->keylen != (head)->hh.tbl->num_items) {       \
+            HASH_OOPS("invalid hh item count %d, actual %d\n",           \
+                (head)->hh.tbl->num_items, (head)->hh.tbl->keylen );     \
+        }                                                                \
+        /* traverse hh in app order; check next/prev integrity, count */ \
+        (head)->hh.tbl->keylen = 0;   /* item counter */                 \
+        (head)->hh.tbl->key = NULL;  /* app prev */                      \
+        (head)->hh.tbl->hh =  &(head)->hh;                               \
+        while ((head)->hh.tbl->hh) {                                     \
+           (head)->hh.tbl->keylen++;                                     \
+           if ((head)->hh.tbl->key !=(char*)((head)->hh.tbl->hh->prev)) {\
+              HASH_OOPS("invalid prev %x, actual %x\n",                  \
+                    (head)->hh.tbl->hh->prev,                            \
+                    (head)->hh.tbl->key );                               \
+           }                                                             \
+           (head)->hh.tbl->key = (head)->hh.tbl->hh->elmt;               \
+           (head)->hh.tbl->hh = ( (head)->hh.tbl->hh->next ?             \
+             (UT_hash_handle*)((long)((head)->hh.tbl->hh->next) +        \
+                               (head)->hh.tbl->hho)                      \
+                                 : NULL );                               \
+        }                                                                \
+        if ((head)->hh.tbl->keylen != (head)->hh.tbl->num_items) {       \
+            HASH_OOPS("invalid app item count %d, actual %d\n",          \
+                (head)->hh.tbl->num_items, (head)->hh.tbl->keylen );     \
+        }                                                                \
+    }                                                                    \
+} while (0)
+#else
+#define HASH_FSCK(head) 
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                           \
+    (head)->hh.tbl->keylen = fieldlen;                                   \
+    write(HASH_EMIT_KEYS, &((head)->hh.tbl->keylen), sizeof(int));       \
+    write(HASH_EMIT_KEYS, keyptr, fieldlen);          
+#else 
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                    
+#endif
+
+/* default to Jenkins unless specified e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION 
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6 */
+#define HASH_BER(key,keylen,num_bkts,bkt,i,j,k)                        \
+  bkt = 0;                                                             \
+  while (keylen--)  bkt = (bkt * 33) + *key++;                         \
+  bkt &= (num_bkts-1);          
+
+
+/* SAX/FNV/OAT/JEN/JSW hash functions are macro variants of those listed at 
+ * http://eternallyconfuzzled.com/tuts/hashing.html (thanks Julienne Walker) */
+#define HASH_SAX(key,keylen,num_bkts,bkt,i,j,k)                        \
+  bkt = 0;                                                             \
+  for(i=0; i < keylen; i++)                                            \
+      bkt ^= (bkt << 5) + (bkt >> 2) + key[i];                         \
+  bkt &= (num_bkts-1);          
+
+#define HASH_FNV(key,keylen,num_bkts,bkt,i,j,k)                        \
+  bkt = 2166136261UL;                                                  \
+  for(i=0; i < keylen; i++)                                            \
+      bkt = (bkt * 16777619) ^ key[i];                                 \
+  bkt &= (num_bkts-1);
+ 
+#define HASH_OAT(key,keylen,num_bkts,bkt,i,j,k)                        \
+  bkt = 0;                                                             \
+  for(i=0; i < keylen; i++) {                                          \
+      bkt += key[i];                                                   \
+      bkt += (bkt << 10);                                              \
+      bkt ^= (bkt >> 6);                                               \
+  }                                                                    \
+  bkt += (bkt << 3);                                                   \
+  bkt ^= (bkt >> 11);                                                  \
+  bkt += (bkt << 15);                                                  \
+  bkt &= (num_bkts-1);
+
+#define HASH_JEN_MIX(a,b,c)                                            \
+{                                                                      \
+  a -= b; a -= c; a ^= ( c >> 13 );                                    \
+  b -= c; b -= a; b ^= ( a << 8 );                                     \
+  c -= a; c -= b; c ^= ( b >> 13 );                                    \
+  a -= b; a -= c; a ^= ( c >> 12 );                                    \
+  b -= c; b -= a; b ^= ( a << 16 );                                    \
+  c -= a; c -= b; c ^= ( b >> 5 );                                     \
+  a -= b; a -= c; a ^= ( c >> 3 );                                     \
+  b -= c; b -= a; b ^= ( a << 10 );                                    \
+  c -= a; c -= b; c ^= ( b >> 15 );                                    \
+}
+
+#define HASH_JEN(key,keylen,num_bkts,bkt,i,j,k)                        \
+  bkt = 0xfeedbeef;                                                    \
+  i = j = 0x9e3779b9;                                                  \
+  k = keylen;                                                          \
+  while (k >= 12) {                                                    \
+     i += (key[0] + ( (unsigned)key[1] << 8 )                          \
+       + ( (unsigned)key[2] << 16 )                                    \
+       + ( (unsigned)key[3] << 24 ) );                                 \
+     j += (key[4] + ( (unsigned)key[5] << 8 )                          \
+       + ( (unsigned)key[6] << 16 )                                    \
+       + ( (unsigned)key[7] << 24 ) );                                 \
+   bkt += (key[8] + ( (unsigned)key[9] << 8 )                          \
+       + ( (unsigned)key[10] << 16 )                                   \
+       + ( (unsigned)key[11] << 24 ) );                                \
+                                                                       \
+     HASH_JEN_MIX(i, j, bkt);                                          \
+                                                                       \
+     key += 12;                                                        \
+     k -= 12;                                                          \
+  }                                                                    \
+  bkt += keylen;                                                       \
+  switch ( k ) {                                                       \
+     case 11: bkt += ( (unsigned)key[10] << 24 );                      \
+     case 10: bkt += ( (unsigned)key[9] << 16 );                       \
+     case 9:  bkt += ( (unsigned)key[8] << 8 );                        \
+     case 8:  j += ( (unsigned)key[7] << 24 );                         \
+     case 7:  j += ( (unsigned)key[6] << 16 );                         \
+     case 6:  j += ( (unsigned)key[5] << 8 );                          \
+     case 5:  j += key[4];                                             \
+     case 4:  i += ( (unsigned)key[3] << 24 );                         \
+     case 3:  i += ( (unsigned)key[2] << 16 );                         \
+     case 2:  i += ( (unsigned)key[1] << 8 );                          \
+     case 1:  i += key[0];                                             \
+  }                                                                    \
+  HASH_JEN_MIX(i, j, bkt);                                             \
+  bkt &= (num_bkts-1);
+
+#define HASH_JSW(key,keylen,num_bkts,bkt,i,j,k)                        \
+  bkt = 16777551;                                                      \
+  for(i=0; i < keylen; i++) {                                          \
+      bkt = (bkt << 1 | bkt >> 31) ^                                   \
+      *(int*)((long)(                                                  \
+      "\xe9\x81\x51\xe4\x84\x9d\x32\xd9\x2d\xda\xca\x94\xa7\x85\x1e"   \
+      "\x28\xfe\xa3\x18\x60\x28\x45\xa6\x48\x67\xdb\xd5\xa2\x91\x4d"   \
+      "\x1a\x2f\x97\x37\x82\xd8\xe9\x1c\xb7\x7b\x3c\xa5\x4c\x23\x2"    \
+      "\x42\x85\x20\x78\x6c\x6\x67\x6f\xa5\xcb\x53\x8c\xe1\x1f\x12"    \
+      "\x66\xcb\xa0\xbe\x47\x59\x8\x20\xd5\x31\xd9\xdc\xcc\x27\xc3"    \
+      "\x4d\x8\x9f\xb3\x50\x8\x90\x4f\x1f\x20\x60\xb8\xe2\x7b\x63"     \
+      "\x49\xc0\x64\xc7\xaf\xc9\x81\x9c\x5f\x7d\x45\xc5\xe4\xe4\x86"   \
+      "\xaf\x1a\x15\x6c\x9b\xc3\x7c\xc5\x88\x2b\xf3\xd9\x72\x76\x47"   \
+      "\x56\xe6\x8c\xd1\x6c\x94\x41\x59\x4d\xe2\xd7\x44\x9a\x55\x5e"   \
+      "\xee\x9d\x7c\x8f\x21\x57\x10\x77\xf7\x4b\xd8\x7e\xc0\x4d\xba"   \
+      "\x1f\x96\x2a\x60\x13\xae\xab\x58\x70\xe5\x23\x62\x2b\x63\xb6"   \
+      "\x42\x8e\x8f\x57\xf2\xfa\x47\x37\x91\xac\x11\x3d\x9a\x85\x73"   \
+      "\x9e\x39\x65\xc8\xd4\x5b\xaa\x35\x72\x5f\x40\x31\x9a\xb0\xdd"   \
+      "\xa9\x2c\x16\xa3\x32\xef\xcb\x8c\x80\x33\x60\xd\x85\xce\x22"    \
+      "\x8c\x28\x6\x7f\xff\xf6\x8a\x5f\x21\x8e\xf2\xd0\xd9\x63\x66"    \
+      "\x22\xe8\xe6\x3\x39\xfd\x10\x69\xce\x6c\xc4\xde\xf3\x87\x56"    \
+      "\xc8\x4a\x31\x51\x58\xc5\x62\x30\x8e\xd\xd5\x2f\x7c\x24\xca"    \
+      "\xd1\x12\x1b\x3a\x3e\x95\x99\xa\x7\xc1\x83\xd0\x4f\x97\x8c"     \
+      "\xf1\xb0\x9c\xd8\xb9\x72\xd7\x3e\x6b\x66\x83\x8e\xe9\x86\xad"   \
+      "\xfa\xc2\xe\x4\xb5\x7b\x5d\x0\xbc\x47\xbe\x4\x69\xfa\xd1"       \
+      "\x29\x5c\x77\x38\xfc\x88\xeb\xd5\xe1\x17\x54\xf6\xe5\xb3\xae"   \
+      "\xc7\x14\xb6\x4b\xa6\x42\x4b\xa3\xdf\xa5\xcf\xdb\xad\xcd\x2c"   \
+      "\xa3\x3\x13\xc0\x42\x5d\x6e\x3c\xfe\xd8\xeb\xa7\x96\x47\x2b"    \
+      "\x61\xb3\x70\xc9\x6d\xff\x1a\x82\x65\xdc\x92\x4b\x1a\x52\x75"   \
+      "\xa5\x61\x55\x2b\xe\x7\xde\x1e\x71\xc5\x12\x34\x59\x4f\x19"     \
+      "\x2\x9\xb6\x5\xe6\x7b\xad\xb6\x92\xfb\x84\x32\xf1\x45\x6c"      \
+      "\xec\x1a\xcb\x39\x32\x2\x47\x51\xd6\xc8\x9d\xd0\xb1\xdb\xa8"    \
+      "\x90\x4c\x65\x5a\x77\x1f\xca\x74\x8e\x3b\xce\x76\x55\x8b\x78"   \
+      "\x3c\xf3\x19\x8f\xe1\xc3\xa9\x8a\xc8\xf3\x14\x30\x4e\x77\xe9"   \
+      "\xd5\x6a\xcb\x96\x2f\x31\x35\xff\x6b\x10\x92\xf7\xc4\x33\xb8"   \
+      "\x76\x35\x6\xf\x82\x1c\xfa\x1f\x92\x47\xa1\xf9\x7e\xe5\x51"     \
+      "\xee\x63\xaa\x9a\x38\xa3\xa1\x86\xbf\xf0\xe8\x29\xe1\x19\x83"   \
+      "\xff\x36\x3c\x26\x15\x89\x36\x22\x93\x41\x3e\x63\x36\x34\x4c"   \
+      "\xda\x18\xd4\x18\xd8\xc8\x8a\x10\x1f\x14\x4c\x7f\x79\xfc\x46"   \
+      "\xbb\xc8\x24\x51\xc7\xe4\xfb\xc0\x78\xb1\xe9\xac\xf1\x3d\x55"   \
+      "\x51\x9c\x8\xf0\xa6\x3\xcb\x91\xc6\xf4\xe2\xd4\xe5\x18\x61"     \
+      "\xfc\x8f\x8a\xce\x89\x33\xcd\xf\x7d\x50\xa0\x7d\x3f\xac\x49"    \
+      "\xe1\x71\x92\xc7\x8d\xc0\xd0\x6e\xe4\xf7\xcd\xc1\x47\x9f\x99"   \
+      "\xd5\x7\x20\xad\x64\xdb\xab\x44\xd4\x8\xc6\x9a\xa4\xa7\x7c"     \
+      "\x9b\x13\xe4\x9c\x88\xec\xc4\xcb\xe1\x3f\x5\x5\xf\xd\x3a"       \
+      "\x75\xed\xfa\xc0\x23\x34\x74\xfd\xca\x1c\x74\x77\x29\xc8\xb6"   \
+      "\xe2\xbb\xa1\xa\x2e\xae\x65\x3e\xcb\xf5\x5e\xe0\x29\x4c\xfa"    \
+      "\xab\x35\xea\x7\x9f\xb3\x3b\x9c\x4e\x86\xe8\x5b\x76\x11\xf1"    \
+      "\xbf\x7f\x73\x34\x71\x9\x2d\x2a\x60\x8f\x14\x12\xba\x26\x84"    \
+      "\xb9\x94\xa9\x59\x38\x25\xfd\x77\xc3\xe5\x86\xc4\x3\xda\x32"    \
+      "\x30\xd8\x84\x81\x83\x14\x8c\x24\xee\x51\xa9\x92\x61\xb2\xeb"   \
+      "\xce\xac\x34\xc1\xad\x24\x74\xce\xf9\xce\x5c\xfd\x45\x69\x1d"   \
+      "\xc6\xc2\xaf\x7c\x8d\x5\x52\xb5\x88\x2f\x9f\xee\x6b\x5f\xbd"    \
+      "\xfe\x22\x6\x47\xa2\xc8\x25\x37\x67\x44\x4c\xe\xfe\x7e\x5a"     \
+      "\x36\x7f\x18\x83\x8f\x82\x87\x3b\xbf\xb8\xd2\x37\xff\x52\x60"   \
+      "\xb5\xf3\xd\x20\x80\xcc\xb2\x7a\xdd\xc2\x94\xbc\xe3\xb1\x87"    \
+      "\x3e\x49\x57\xcc\xe9\x5a\xea\xb4\xe\xdf\xa6\x8f\x70\x60\x32"    \
+      "\xb\x7d\x74\xf5\x46\xb6\x93\xc2\x5\x92\x72\xfc\xd9\xd2\xe5"     \
+      "\x90\x36\x2a\xd4\xf9\x50\x33\x52\xa5\xcc\xcf\x14\x9e\xdc\x4f"   \
+      "\xb7\x7d\xcf\x25\xdb\xc0\x46\xdb\xea\xe\x27\xc8\x18\x40\x39"    \
+      "\xbd\xec\x48\xa3\xfa\x87\xa3\x18\x68\xfc\x7a\x44\xa8\xc5\x8c"   \
+      "\x45\x81\x70\x72\x14\x70\xf9\x40\xc8\xe7\x41\xcb\xde\xd\x4e"    \
+      "\x35\x4d\xcd\xe2\x40\xa3\x2e\xbb\xb7\x50\x6c\x26\xb8\xbe\x2a"   \
+      "\x36\x8e\x23\xb\xa\xfe\xed\xa\xe7\xa0\x16\x73\xad\x24\x51"      \
+      "\x7f\xda\x9d\xd7\x9f\x18\xe6\xa8\xe4\x98\xbc\x62\x77\x55\x60"   \
+      "\x88\x16\x25\xbf\x95\xad\xea\xe1\x87\x18\x35\x9e\x7c\x51\xee"   \
+      "\xc0\x80\x8b\xb8\x37\xfd\x95\xfe\x87\x15\xf4\x97\xd5\x61\x4f"   \
+      "\x97\xfa\xaf\x48\xd\x5b\x84\x2d\xdb\x15\xf2\xb4\x17\x4f\x41"    \
+      "\x31\x58\x32\x93\xc1\x52\x34\xa6\x17\xd\x56\x5\xee\xfb\xfb"     \
+      "\x2d\x69\x14\xbe\x24\x94\x8\xb0\xfc\x9f\x2\x95\x88\x7d\xd6"     \
+      "\xe7\xa4\x5b\xbb\xf2\x7d\xd8\xa5\xd2\x7c\x9\x62\x22\x5\x53"     \
+      "\xd0\x67\xeb\x68\xfc\x82\x80\xf\xc9\x73\x76\x36\xb8\x13\x9f"    \
+      "\xb1\xf1\xee\x61\x12\xe7\x5d\x75\x65\xb8\x84\x17\xb\x7b\x28"    \
+      "\x4c\xb7\xda\xbb" )                                             \
+      + ( (unsigned char)key[i] * sizeof(int) ));                      \
+  }                                                                    \
+  bkt &= (num_bkts-1);
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) 
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(hh,head,keyptr,keylen_in,out)               \
+out = (head.hh_head) ? (head.hh_head->elmt) : NULL;                  \
+while (out) {                                                        \
+    if (out->hh.keylen == keylen_in) {                               \
+        if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
+    }                                                                \
+    out= (out->hh.hh_next) ? (out->hh.hh_next->elmt) : NULL;         \
+}
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(hh,head,add)                                 \
+ head.count++;                                                       \
+ add->hh.hh_next = head.hh_head;                                     \
+ add->hh.hh_prev = NULL;                                             \
+ if (head.hh_head) head.hh_head->hh_prev = &add->hh;                 \
+ head.hh_head=&add->hh;                                              \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
+     && add->hh.tbl->noexpand != 1) {                                \
+       HASH_EXPAND_BUCKETS(add->hh.tbl)                              \
+ }
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,delptr)                              \
+    (head).count--;                                                  \
+    if ((head).hh_head->elmt == delptr) {                            \
+      (head).hh_head = delptr->hh.hh_next;                           \
+    }                                                                \
+    if (delptr->hh.hh_prev) {                                        \
+        delptr->hh.hh_prev->hh_next = delptr->hh.hh_next;            \
+    }                                                                \
+    if (delptr->hh.hh_next) {                                        \
+        delptr->hh.hh_next->hh_prev = delptr->hh.hh_prev;            \
+    }                                                                
+
+#define HASH_EXPAND_BUCKETS(tbl)                                     \
+    tbl->new_buckets = (UT_hash_bucket*)uthash_bkt_malloc(           \
+             2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));  \
+    if (!tbl->new_buckets) { uthash_fatal( "out of memory"); }       \
+    memset(tbl->new_buckets, 0,                                      \
+            2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));   \
+    tbl->bkt_ideal= (tbl->num_items /  tbl->num_buckets*2) +         \
+                   ((tbl->num_items % (tbl->num_buckets*2)) ? 1 : 0);\
+    tbl->sum_of_deltas = 0;                                          \
+    for(tbl->bkt_i = 0; tbl->bkt_i < tbl->num_buckets; tbl->bkt_i++) \
+    {                                                                \
+        tbl->hh = tbl->buckets[ tbl->bkt_i ].hh_head;                \
+        while (tbl->hh) {                                            \
+           tbl->hh_nxt = tbl->hh->hh_next;                           \
+           tbl->key = tbl->hh->key;                                  \
+           tbl->keylen = tbl->hh->keylen;                            \
+           HASH_FCN(tbl->key,tbl->keylen,tbl->num_buckets*2,tbl->bkt,\
+                   tbl->i,tbl->j,tbl->k);                            \
+           tbl->newbkt = &(tbl->new_buckets[ tbl->bkt ]);            \
+           if (++(tbl->newbkt->count) > tbl->bkt_ideal) {            \
+             tbl->sum_of_deltas++;                                   \
+             tbl->newbkt->expand_mult = tbl->newbkt->count /         \
+                                        tbl->bkt_ideal;              \
+           }                                                         \
+           tbl->hh->hh_prev = NULL;                                  \
+           tbl->hh->hh_next = tbl->newbkt->hh_head;                  \
+           if (tbl->newbkt->hh_head) tbl->newbkt->hh_head->hh_prev = \
+                tbl->hh;                                             \
+           tbl->newbkt->hh_head = tbl->hh;                           \
+           tbl->hh = tbl->hh_nxt;                                    \
+        }                                                            \
+    }                                                                \
+    tbl->num_buckets *= 2;                                           \
+    uthash_bkt_free( tbl->buckets );                                 \
+    tbl->buckets = tbl->new_buckets;                                 \
+    tbl->new_hash_q = (1<<16) - ((tbl->sum_of_deltas << 16) / tbl->num_items); \
+    if (tbl->hash_q < (1<<15) && tbl->new_hash_q < (1<<15)) {                \
+        tbl->noexpand=1;                                             \
+        uthash_noexpand_fyi(tbl);                                    \
+    }                                                                \
+    tbl->hash_q = tbl->new_hash_q;                                   \
+    uthash_expand_fyi(tbl);                                         
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+#define HASH_SORT(head,cmpfcn)                                                 \
+  if (head) {                                                                  \
+      (head)->hh.tbl->insize = 1;                                              \
+      (head)->hh.tbl->looping = 1;                                             \
+      (head)->hh.tbl->list = &((head)->hh);                                    \
+      while ((head)->hh.tbl->looping) {                                        \
+          (head)->hh.tbl->p = (head)->hh.tbl->list;                            \
+          (head)->hh.tbl->list = NULL;                                         \
+          (head)->hh.tbl->tale = NULL;                                         \
+          (head)->hh.tbl->nmerges = 0;                                         \
+          while ((head)->hh.tbl->p) {                                          \
+              (head)->hh.tbl->nmerges++;                                       \
+              (head)->hh.tbl->q = (head)->hh.tbl->p;                           \
+              (head)->hh.tbl->psize = 0;                                       \
+              for ( (head)->hh.tbl->i = 0;                                     \
+                    (head)->hh.tbl->i  < (head)->hh.tbl->insize;               \
+                    (head)->hh.tbl->i++ ) {                                    \
+                  (head)->hh.tbl->psize++;                                     \
+                  (head)->hh.tbl->q = (((head)->hh.tbl->q->next) ?             \
+                      ((void*)(((long)((head)->hh.tbl->q->next)) +             \
+                      (head)->hh.tbl->hho)) : NULL);                           \
+                  if (! ((head)->hh.tbl->q) ) break;                           \
+              }                                                                \
+              (head)->hh.tbl->qsize = (head)->hh.tbl->insize;                  \
+              while (((head)->hh.tbl->psize > 0) ||                            \
+                      (((head)->hh.tbl->qsize > 0) && (head)->hh.tbl->q )) {   \
+                  if ((head)->hh.tbl->psize == 0) {                            \
+                      (head)->hh.tbl->e = (head)->hh.tbl->q;                   \
+                      (head)->hh.tbl->q = (((head)->hh.tbl->q->next) ?         \
+                          ((void*)(((long)((head)->hh.tbl->q->next)) +         \
+                          (head)->hh.tbl->hho)) : NULL);                       \
+                      (head)->hh.tbl->qsize--;                                 \
+                  } else if ( ((head)->hh.tbl->qsize == 0) ||                  \
+                             !((head)->hh.tbl->q) ) {                          \
+                      (head)->hh.tbl->e = (head)->hh.tbl->p;                   \
+                      (head)->hh.tbl->p = (((head)->hh.tbl->p->next) ?         \
+                          ((void*)(((long)((head)->hh.tbl->p->next)) +         \
+                          (head)->hh.tbl->hho)) : NULL);                       \
+                      (head)->hh.tbl->psize--;                                 \
+                  } else if ((                                                 \
+                      cmpfcn((head)->hh.tbl->p->elmt,(head)->hh.tbl->q->elmt)) \
+                          <= 0) {                                              \
+                      (head)->hh.tbl->e = (head)->hh.tbl->p;                   \
+                      (head)->hh.tbl->p = (((head)->hh.tbl->p->next) ?         \
+                          ((void*)(((long)((head)->hh.tbl->p->next)) +         \
+                          (head)->hh.tbl->hho)) : NULL);                       \
+                      (head)->hh.tbl->psize--;                                 \
+                  } else {                                                     \
+                      (head)->hh.tbl->e = (head)->hh.tbl->q;                   \
+                      (head)->hh.tbl->q = (((head)->hh.tbl->q->next) ?         \
+                          ((void*)(((long)((head)->hh.tbl->q->next)) +         \
+                                   (head)->hh.tbl->hho)) : NULL);              \
+                      (head)->hh.tbl->qsize--;                                 \
+                  }                                                            \
+                  if ( (head)->hh.tbl->tale ) {                                \
+                      (head)->hh.tbl->tale->next = (((head)->hh.tbl->e) ?      \
+                               ((head)->hh.tbl->e->elmt) : NULL);              \
+                  } else {                                                     \
+                      (head)->hh.tbl->list = (head)->hh.tbl->e;                \
+                  }                                                            \
+                  (head)->hh.tbl->e->prev = (((head)->hh.tbl->tale) ?          \
+                                 ((head)->hh.tbl->tale->elmt) : NULL);         \
+                  (head)->hh.tbl->tale = (head)->hh.tbl->e;                    \
+              }                                                                \
+              (head)->hh.tbl->p = (head)->hh.tbl->q;                           \
+          }                                                                    \
+          (head)->hh.tbl->tale->next = NULL;                                   \
+          if ( (head)->hh.tbl->nmerges <= 1 ) {                                \
+              (head)->hh.tbl->looping=0;                                       \
+              (head)->hh.tbl->tail = (head)->hh.tbl->tale;                     \
+              (head) = (head)->hh.tbl->list->elmt;                             \
+          }                                                                    \
+          (head)->hh.tbl->insize *= 2;                                         \
+      }                                                                        \
+      HASH_FSCK(head);                                                         \
+ }
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;  
+   unsigned expand_mult;  
+} UT_hash_bucket;
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets;
+   unsigned num_items;
+   int noexpand;  /* when set, inhibits expansion of buckets for this hash  */
+   int hash_q; /* measures the evenness of the items among buckets (0-1) */
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   char *name;    /* macro-stringified name of list head, used by libut     */
+   int hho;
+   /* scratch */
+   unsigned bkt;
+   char *key;
+   size_t keylen;
+   int i,j,k;
+   /* scratch for bucket expansion */
+   UT_hash_bucket *new_buckets, *newbkt;
+   struct UT_hash_handle *hh, *hh_nxt;
+   unsigned bkt_i, bkt_ideal, sum_of_deltas;
+   int new_hash_q;
+   /* scratch for sort */
+   int looping,nmerges,insize,psize,qsize;
+   struct UT_hash_handle *p, *q, *e, *list, *tale;
+   
+} UT_hash_table;
+
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *elmt;                       /* ptr to enclosing element       */
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   void *key;                        /* ptr to enclosing struct's key  */
+   size_t keylen;                       /* enclosing struct's key len     */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff-tree e9717094756042e4cb69000bea8ed14d8bbd2645 (from e62950dd443510e4a41f73e12c79a73c0fee297d)
Author: Rob Taylor <rob.taylor at codethink.co.uk>
Date:   Fri Jun 22 15:47:30 2007 +0100

    [PATCH] libhal support for addon singletons
    
    Adds api to libhal for supporting addon singletons:
     - libhal_device_singleton_addon_is_ready for singletons to signal readyness.
     - libhal_ctx_set_singleton_device_added and
       libhal_ctx_set_singleton_device_removed for setting a callback on singleton
       DeviceAdded/Removed. The properties of the new device are passed as a
       LibHalPropertySet to the callback.
     - a number of direct accessors for properties in a LibHalPropertySet

diff --git a/libhal/libhal.c b/libhal/libhal.c
index c8e41aa..1f531b3 100644
--- a/libhal/libhal.c
+++ b/libhal/libhal.c
@@ -4,6 +4,8 @@
  * libhal.c : HAL daemon C convenience library
  *
  * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Sjoerd Simons, <sjoerd at luon.net>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -258,6 +260,12 @@ struct LibHalContext_s {
         /** An interface lock is released  */
         LibHalInterfaceLockReleased interface_lock_released;
 
+	/** Singleton device added */
+	LibHalSingletonDeviceAdded singleton_device_added;
+
+	/** Singleton device removed*/
+	LibHalSingletonDeviceRemoved singleton_device_removed;
+
 	void *user_data;                      /**< User data */
 };
 
@@ -383,66 +391,17 @@ libhal_property_fill_value_from_variant 
 	return TRUE;
 }
 
-/**
- * libhal_device_get_all_properties:
- * @ctx: the context for the connection to hald
- * @udi: the Unique id of device
- * @error: pointer to an initialized dbus error object for returning errors or NULL
- *
- * Retrieve all the properties on a device.
- *
- * Returns: An object represent all properties. Must be freed with libhal_free_property_set().
- */
-LibHalPropertySet *
-libhal_device_get_all_properties (LibHalContext *ctx, const char *udi, DBusError *error)
-{	
-	DBusMessage *message;
-	DBusMessage *reply;
-	DBusMessageIter reply_iter;
-	DBusMessageIter dict_iter;
+static LibHalPropertySet *
+get_property_set (DBusMessageIter *iter)
+{
 	LibHalPropertySet *result;
 	LibHalProperty *p_last;
-	DBusError _error;
-
-	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
-	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", NULL);
-
-	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
-						"org.freedesktop.Hal.Device",
-						"GetAllProperties");
-
-	if (message == NULL) {
-		fprintf (stderr,
-			 "%s %d : Couldn't allocate D-BUS message\n",
-			 __FILE__, __LINE__);
-		return NULL;
-	}
-
-	dbus_error_init (&_error);
-	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
-							   message, -1,
-							   &_error);
-
-	dbus_move_error (&_error, error);
-	if (error != NULL && dbus_error_is_set (error)) {
-		fprintf (stderr,
-			 "%s %d : %s\n",
-			 __FILE__, __LINE__, error->message);
-
-		dbus_message_unref (message);
-		return NULL;
-	}
-
-	if (reply == NULL) {
-		dbus_message_unref (message);
-		return NULL;
-	}
-
-	dbus_message_iter_init (reply, &reply_iter);
+	DBusMessageIter dict_iter;
 
 	result = malloc (sizeof (LibHalPropertySet));
 	if (result == NULL) 
 		goto oom;
+
 /*
     result->properties = malloc(sizeof(LibHalProperty)*result->num_properties);
     if( result->properties==NULL )
@@ -455,16 +414,14 @@ libhal_device_get_all_properties (LibHal
 	result->properties_head = NULL;
 	result->num_properties = 0;
 
-	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_ARRAY  &&
-	    dbus_message_iter_get_element_type (&reply_iter) != DBUS_TYPE_DICT_ENTRY) {
+	if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_ARRAY  &&
+	    dbus_message_iter_get_element_type (iter) != DBUS_TYPE_DICT_ENTRY) {
 		fprintf (stderr, "%s %d : error, expecting an array of dict entries\n",
 			 __FILE__, __LINE__);
-		dbus_message_unref (message);
-		dbus_message_unref (reply);
 		return NULL;
 	}
 
-	dbus_message_iter_recurse (&reply_iter, &dict_iter);
+	dbus_message_iter_recurse (iter, &dict_iter);
 
 	p_last = NULL;
 
@@ -511,9 +468,6 @@ libhal_device_get_all_properties (LibHal
 		dbus_message_iter_next (&dict_iter);
 	}
 
-	dbus_message_unref (message);
-	dbus_message_unref (reply);
-
 	return result;
 
 oom:
@@ -525,6 +479,69 @@ oom:
 }
 
 /**
+ * libhal_device_get_all_properties:
+ * @ctx: the context for the connection to hald
+ * @udi: the Unique id of device
+ * @error: pointer to an initialized dbus error object for returning errors or NULL
+ *
+ * Retrieve all the properties on a device.
+ *
+ * Returns: An object represent all properties. Must be freed with libhal_free_property_set().
+ */
+LibHalPropertySet *
+libhal_device_get_all_properties (LibHalContext *ctx, const char *udi, DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessage *reply;
+	DBusMessageIter reply_iter;
+	LibHalPropertySet *result;
+	DBusError _error;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
+	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", NULL);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
+						"org.freedesktop.Hal.Device",
+						"GetAllProperties");
+
+	if (message == NULL) {
+		fprintf (stderr,
+			 "%s %d : Couldn't allocate D-BUS message\n",
+			 __FILE__, __LINE__);
+		return NULL;
+	}
+
+	dbus_error_init (&_error);
+	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
+							   message, -1,
+							   &_error);
+
+	dbus_move_error (&_error, error);
+	if (error != NULL && dbus_error_is_set (error)) {
+		fprintf (stderr,
+			 "%s %d : %s\n",
+			 __FILE__, __LINE__, error->message);
+
+		dbus_message_unref (message);
+		return NULL;
+	}
+
+	if (reply == NULL) {
+		dbus_message_unref (message);
+		return NULL;
+	}
+
+	dbus_message_iter_init (reply, &reply_iter);
+
+	result = get_property_set (&reply_iter);
+
+	dbus_message_unref (message);
+	dbus_message_unref (reply);
+
+	return result;
+}
+
+/**
  * libhal_property_set_sort:
  * @set: property-set to sort
  *
@@ -616,9 +633,150 @@ libhal_property_set_get_num_elems (LibHa
 	return num_elems;
 }
 
+static LibHalProperty *
+property_set_lookup (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p;
+
+	for (p = set->properties_head; p != NULL; p = p->next) {
+		if (strcmp (p->key, key) == 0)
+			return p;
+	}
+	return NULL;
+}
+
+/**
+ * libhal_ps_get_type:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the type of a given property. 
+ *
+ * Returns: the #LibHalPropertyType of the given property, 
+ * LIBHAL_PROPERTY_TYPE_INVALID if property is not in the set
+ */
+LibHalPropertyType
+libhal_ps_get_type (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p) return p->type;
+	else return LIBHAL_PROPERTY_TYPE_INVALID;
+}
+
+/**
+ * libhal_ps_get_string:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the value of a property of type string.
+ *
+ * Returns: UTF8 nul-terminated string. This pointer is only valid
+ * until libhal_free_property_set() is invoked on the property set
+ * this property belongs to. NULL if property is not in the set or not a string
+ */
+const char *
+libhal_ps_get_string  (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p && p->type == LIBHAL_PROPERTY_TYPE_STRING)
+		return p->v.str_value;
+	else return NULL;
+}
+
+/**
+ * libhal_ps_get_int:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the value of a property of type signed integer. 
+ *
+ * Returns: property value (32-bit signed integer)
+ */
+dbus_int32_t
+libhal_ps_get_int32 (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p && p->type == LIBHAL_PROPERTY_TYPE_INT32)
+		return p->v.int_value;
+	else return 0;
+}
+
+/**
+ * libhal_ps_get_uint64:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the value of a property of type unsigned integer. 
+ *
+ * Returns: property value (64-bit unsigned integer)
+ */
+dbus_uint64_t
+libhal_ps_get_uint64 (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p && p->type == LIBHAL_PROPERTY_TYPE_UINT64)
+		return p->v.uint64_value;
+	else return 0;
+}
+
+/**
+ * libhal_ps_get_double:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the value of a property of type double.
+ *
+ * Returns: property value (IEEE754 double precision float)
+ */
+double
+libhal_ps_get_double (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p && p->type == LIBHAL_PROPERTY_TYPE_DOUBLE)
+		return p->v.double_value;
+	else return 0.0;
+}
+
+/**
+ * libhal_ps_get_bool:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the value of a property of type bool. 
+ *
+ * Returns: property value (bool)
+ */
+dbus_bool_t
+libhal_ps_get_bool (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p && p->type == LIBHAL_PROPERTY_TYPE_BOOLEAN)
+		return p->v.bool_value;
+	else return FALSE;
+}
+
+/**
+ * libhal_ps_get_strlist:
+ * @set: property set
+ * @key: name of property to inspect
+ *
+ * Get the value of a property of type string list. 
+ *
+ * Returns: pointer to array of strings, this is owned by the property set
+ */
+const char *const *
+libhal_ps_get_strlist (const LibHalPropertySet *set, const char *key)
+{
+	LibHalProperty *p = property_set_lookup (set, key);
+	if (p && p->type == LIBHAL_PROPERTY_TYPE_STRLIST)
+		return (const char *const *) p->v.strlist_value;
+	else return NULL;
+}
+
 
 /**
  * libhal_psi_init:
+ * 1;3B
  * @iter: iterator object
  * @set: property set to iterate over
  *
@@ -780,6 +938,56 @@ libhal_psi_get_strlist (LibHalPropertySe
 	return iter->cur_prop->v.strlist_value;
 }
 
+static DBusHandlerResult
+singleton_device_changed (LibHalContext *ctx, DBusConnection *connection, DBusMessage *msg, dbus_bool_t added)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	LibHalPropertySet *set;
+	const char *udi;
+
+	dbus_message_iter_init (msg, &iter);
+
+	/* First should be the device UDI */
+	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+		goto malformed;
+
+	dbus_message_iter_get_basic (&iter, &udi);
+
+	dbus_message_iter_next (&iter);
+
+	/* then the property set*/
+	set = get_property_set (&iter);
+
+	if (!set)
+		goto malformed;
+	if (added)
+		(ctx->singleton_device_added)(ctx, udi, set);
+	else
+		(ctx->singleton_device_removed)(ctx, udi, set);
+
+	libhal_free_property_set (set);
+
+	reply = dbus_message_new_method_return (msg);
+	if (reply == NULL)
+		goto oom;
+
+	if (!dbus_connection_send (connection, reply, NULL)) {
+		dbus_message_unref (reply);
+		goto oom;
+	}
+
+	dbus_message_unref (reply);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+
+malformed:
+	fprintf (stderr, "%s %d : singlton device changed message malformed\n", __FILE__, __LINE__);
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+oom:
+	fprintf (stderr, "%s %d : error allocating memory\n", __FILE__, __LINE__);
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
 
 static DBusHandlerResult
 filter_func (DBusConnection * connection,
@@ -796,7 +1004,12 @@ filter_func (DBusConnection * connection
 
 	object_path = dbus_message_get_path (message);
 
-	/*printf("*** in filter_func, object_path=%s\n", object_path);*/
+	/*fprintf (stderr, "*** libhal filer_func: connection=%p obj_path=%s interface=%s method=%s\n", 
+		   connection,
+		   dbus_message_get_path (message), 
+		   dbus_message_get_interface (message),
+		   dbus_message_get_member (message));
+        */
 
 	if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager",
 				    "DeviceAdded")) {
@@ -952,8 +1165,20 @@ filter_func (DBusConnection * connection
 			
 		}
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	} else if (dbus_message_is_method_call (message,
+			"org.freedesktop.Hal.SingletonAddon",
+			"DeviceAdded") &&
+		    strcmp (dbus_message_get_path (message),
+			  "/org/freedesktop/Hal/SingletonAddon") == 0) {
+		return singleton_device_changed (ctx, connection, message, TRUE);
+	} else if (dbus_message_is_method_call (message,
+			"org.freedesktop.Hal.SingletonAddon",
+			"DeviceRemoved") &&
+		    strcmp (dbus_message_get_path (message),
+			  "/org/freedesktop/Hal/SingletonAddon") == 0) {
+		return singleton_device_changed (ctx, connection, message, FALSE);
 	}
-	
+
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
@@ -3187,6 +3412,11 @@ libhal_ctx_init_direct (DBusError *error
 		goto out;
 	}
 
+	if (!dbus_connection_add_filter (ctx->connection, filter_func, ctx, NULL)) {
+		return FALSE;
+	}
+
+
 	ctx->is_initialized = TRUE;
 	ctx->is_direct = TRUE;
 
@@ -3361,6 +3591,42 @@ libhal_ctx_set_device_condition (LibHalC
 }
 
 /**
+ * libhal_ctx_set_singleton_device_added:
+ * @ctx: the context for the connection to hald
+ * @callback: the function to call when a device emits a condition
+ *
+ * Set the callback for when a singleton should handle a new device
+ *
+ * Returns: TRUE if callback was successfully set, FALSE otherwise
+ */
+dbus_bool_t
+libhal_ctx_set_singleton_device_added (LibHalContext *ctx, LibHalSingletonDeviceAdded callback)
+{
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+
+	ctx->singleton_device_added = callback;
+	return TRUE;
+}
+
+/**
+ * libhal_ctx_set_singleton_device_removed:
+ * @ctx: the context for the connection to hald
+ * @callback: the function to call when a device emits a condition
+ *
+ * Set the callback for when a singleton should discard a device
+ *
+ * Returns: TRUE if callback was successfully set, FALSE otherwise
+ */
+dbus_bool_t
+libhal_ctx_set_singleton_device_removed (LibHalContext *ctx, LibHalSingletonDeviceRemoved callback)
+{
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+
+	ctx->singleton_device_removed = callback;
+	return TRUE;
+}
+
+/**
  * libhal_string_array_length:
  * @str_array: array of strings to consider
  *
@@ -3554,20 +3820,30 @@ dbus_bool_t libhal_device_emit_condition
 							   error);
 
 	if (error != NULL && dbus_error_is_set (error)) {
+		fprintf (stderr,
+			 "%s %d : Failure sending D-BUS message: %s: %s\n",
+			 __FILE__, __LINE__, error->name, error->message);
 		dbus_message_unref (message);
 		return FALSE;
 	}
 
 	dbus_message_unref (message);
 
-	if (reply == NULL)
+	if (reply == NULL) {
+		fprintf (stderr,
+			 "%s %d : Got no reply\n",
+			 __FILE__, __LINE__);
 		return FALSE;
+	}
 
 	dbus_message_iter_init (reply, &reply_iter);
 	if (dbus_message_iter_get_arg_type (&reply_iter) !=
 		   DBUS_TYPE_BOOLEAN) {
 		dbus_message_unref (message);
 		dbus_message_unref (reply);
+		fprintf (stderr,
+			 "%s %d : Malformed reply\n",
+			 __FILE__, __LINE__);
 		return FALSE;
 	}
 	dbus_message_iter_get_basic (&reply_iter, &result);
@@ -3577,35 +3853,27 @@ dbus_bool_t libhal_device_emit_condition
 	return result;	
 }
 
-/**
- * libhal_device_addon_is_ready:
- * @ctx: the context for the connection to hald
- * @udi: the Unique Device Id
- * @error: pointer to an initialized dbus error object for returning errors or NULL
- *
- * HAL addon's must call this method when they are done initializing the device object. The HAL
- * daemon will wait for all addon's to call this.
- *
- * Can only be used from hald helpers.
- *
- * Returns: TRUE if the HAL daemon received the message, FALSE otherwise
- */
-dbus_bool_t
-libhal_device_addon_is_ready (LibHalContext *ctx, const char *udi, DBusError *error)
+static dbus_bool_t
+addon_is_ready(LibHalContext *ctx, const char *identifier,
+	       dbus_bool_t singleton, DBusError *error)
 {
 	DBusMessage *message;
 	DBusMessageIter iter;
-	DBusMessageIter reply_iter;
 	DBusMessage *reply;
-	dbus_bool_t result;
 
 	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
-	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", FALSE);
 
-	message = dbus_message_new_method_call ("org.freedesktop.Hal",
-						udi,
+        if (singleton) {
+        	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						"/org/freedesktop/Hal/Manager",
+						"org.freedesktop.Hal.Manager",
+						"SingletonAddonIsReady");
+        } else {
+        	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						identifier,
 						"org.freedesktop.Hal.Device",
 						"AddonIsReady");
+        }
 
 	if (message == NULL) {
 		fprintf (stderr,
@@ -3615,7 +3883,9 @@ libhal_device_addon_is_ready (LibHalCont
 	}
 
 	dbus_message_iter_init_append (message, &iter);
-	
+
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &identifier);
+
 	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
 							   message, -1,
 							   error);
@@ -3630,19 +3900,55 @@ libhal_device_addon_is_ready (LibHalCont
 	if (reply == NULL)
 		return FALSE;
 
-	dbus_message_iter_init (reply, &reply_iter);
-	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
-		dbus_message_unref (message);
-		dbus_message_unref (reply);
-		return FALSE;
-	}
-	dbus_message_iter_get_basic (&reply_iter, &result);
-
 	dbus_message_unref (reply);
-	return result;	
+	return TRUE;
+}
+
+
+/**
+ * libhal_device_addon_is_ready:
+ * @ctx: the context for the connection to hald
+ * @udi: the Unique Device Id this addon is handling
+ * @error: pointer to an initialized dbus error object for returning errors or NULL
+ *
+ * HAL addon's must call this method when they are done initializing the device object. The HAL
+ * daemon will wait for all addon's to call this.
+ *
+ * Can only be used from hald helpers.
+ *
+ * Returns: TRUE if the HAL daemon received the message, FALSE otherwise
+ */
+dbus_bool_t
+libhal_device_addon_is_ready (LibHalContext *ctx,
+			      const char *udi,
+			      DBusError *error)
+{
+	return addon_is_ready (ctx, udi, FALSE, error);
 }
 
 /**
+ * libhal_device_singleton_addon_is_ready:
+ * @ctx: the context for the connection to hald
+ * @commandline: commandline singleton was started with
+ * @error: pointer to an initialized dbus error object for returning errors or NULL
+ *
+ * HAL singleton addon's must call this method when they are done initializing the device object. The HAL
+ * daemon will wait for all addon's to call this.
+ *
+ * Can only be used from hald helpers.
+ *
+ * Returns: TRUE if the HAL daemon received the message, FALSE otherwise
+ */
+dbus_bool_t
+libhal_device_singleton_addon_is_ready (LibHalContext *ctx,
+					const char *command_line,
+					DBusError *error)
+{
+	return addon_is_ready (ctx, command_line, TRUE, error);
+}
+
+
+/**
  * libhal_device_claim_interface:
  * @ctx: the context for the connection to hald
  * @udi: the Unique Device Id
diff --git a/libhal/libhal.h b/libhal/libhal.h
index f050d3b..3597ade 100644
--- a/libhal/libhal.h
+++ b/libhal/libhal.h
@@ -4,6 +4,7 @@
  * libhal.h : HAL daemon C convenience library headers
  *
  * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -103,6 +104,9 @@ typedef enum {
 
 
 typedef struct LibHalContext_s LibHalContext;
+typedef struct LibHalProperty_s LibHalProperty;
+typedef struct LibHalPropertySet_s LibHalPropertySet;
+
 
 /** 
  * LibHalIntegrateDBusIntoMainLoop:
@@ -251,6 +255,30 @@ typedef void (*LibHalInterfaceLockReleas
                                              const char *lock_owner,
                                              int         num_locks);
 
+/**
+ * LibHalSingletonDeviceAdded:
+ * @ctx: context for connection to hald
+ * @udi: the Unique Device Id
+ * @properties: the device's properties
+ *
+ * Type for callback for addon singletons when a device is added
+ */
+typedef void (*LibHalSingletonDeviceAdded) (LibHalContext *ctx,
+					    const char *udi,
+					    const LibHalPropertySet *properties);
+
+/**
+ * LibHalSingletonDeviceRemoved:
+ * @ctx: context for connection to hald
+ * @udi: the Unique Device Id
+ * @properties: the device's properties
+ *
+ * Type for callback for addon singletons when a device is added
+ */
+typedef void (*LibHalSingletonDeviceRemoved) (LibHalContext *ctx,
+					    const char *udi,
+					    const LibHalPropertySet *properties);
+
 
 
 /* Create a new context for a connection with hald */
@@ -301,6 +329,12 @@ dbus_bool_t    libhal_ctx_set_interface_
 /* Set the callback for when an interface lock is released  */
 dbus_bool_t    libhal_ctx_set_interface_lock_released (LibHalContext *ctx, LibHalInterfaceLockReleased callback);
 
+/* Set the callback for addon singleton device added */
+dbus_bool_t    libhal_ctx_set_singleton_device_added (LibHalContext *ctx, LibHalSingletonDeviceAdded callback);
+
+/* Set the callback for addon singleton device removed*/
+dbus_bool_t    libhal_ctx_set_singleton_device_removed (LibHalContext *ctx, LibHalSingletonDeviceRemoved callback);
+
 /* Initialize the connection to hald */
 dbus_bool_t    libhal_ctx_init                         (LibHalContext *ctx, DBusError *error);
 
@@ -475,13 +509,6 @@ dbus_bool_t libhal_device_commit_changes
 void libhal_device_free_changeset (LibHalChangeSet *changeset);
 
 
-struct LibHalProperty_s;
-typedef struct LibHalProperty_s LibHalProperty;
-
-struct LibHalPropertySet_s;
-typedef struct LibHalPropertySet_s LibHalPropertySet;
-
-
 /* Retrieve all the properties on a device. */
 LibHalPropertySet *libhal_device_get_all_properties (LibHalContext *ctx, 
 						     const char *udi,
@@ -497,6 +524,28 @@ void libhal_free_property_set (LibHalPro
 /* Get the number of properties in a property set. */
 unsigned int libhal_property_set_get_num_elems (LibHalPropertySet *set);
 
+/* Get type of property. */
+LibHalPropertyType libhal_ps_get_type (const LibHalPropertySet *set, const char *key);
+
+/* Get the value of a property of type string. */
+const char *libhal_ps_get_string  (const LibHalPropertySet *set, const char *key);
+
+/* Get the value of a property of type signed integer. */
+dbus_int32_t libhal_ps_get_int32 (const LibHalPropertySet *set, const char *key);
+
+/* Get the value of a property of type unsigned integer. */
+dbus_uint64_t libhal_ps_get_uint64 (const LibHalPropertySet *set, const char *key);
+
+/* Get the value of a property of type double. */
+double libhal_ps_get_double (const LibHalPropertySet *set, const char *key);
+
+/* Get the value of a property of type bool. */
+dbus_bool_t libhal_ps_get_bool (const LibHalPropertySet *set, const char *key);
+
+/* Get the value of a property of type string list. */
+const char * const *libhal_ps_get_strlist (const LibHalPropertySet *set, const char *key);
+
+
 /** 
  * LibHalPropertySetIterator: 
  * 
@@ -675,6 +724,7 @@ dbus_bool_t libhal_device_claim_interfac
 /* hald waits for all addons to call this function before announcing the addon (for hald helpers only) */
 dbus_bool_t libhal_device_addon_is_ready (LibHalContext *ctx, const char *udi, DBusError *error);
 
+dbus_bool_t libhal_device_singleton_addon_is_ready (LibHalContext *ctx, const char *command_line, DBusError *error);
 
 /* Take a mandatory lock on an interface on a device. */
 dbus_bool_t libhal_device_acquire_interface_lock (LibHalContext *ctx,
diff-tree e62950dd443510e4a41f73e12c79a73c0fee297d (from 664c3a4023adb99d2fce053ce4d706834bb74d01)
Author: Rob Taylor <rob.taylor at codethink.co.uk>
Date:   Thu Jun 7 11:57:05 2007 +0100

    [PATCH] hald core support for singleton addons
    
    Introduces a new property info.addons.singletons for specifything addon singletons for a given device.
    
    A mapping of singleton commandlines to connection and devices serviced is
    maintained in hald_dbus.c. hald now expects a call of SingletonAddonIsReady on
    the interface org.freedesktop.Hal.Manager on /org/freedesktop/Hal/Manager when
    an addon is ready to service requests. When a singleton is ready, DeviceReady
    on the interface org.freedesktop.Hal.SingletonAddon on the object
    /org/freedesktop/Hal/SingletonAddon in the addon is called for all queued
    devices.
    Once a singleton is ready, device adds and removes result in a direct call of
    DeviceReady.

diff --git a/hald/hald.c b/hald/hald.c
index c9bc2f7..394bb1e 100644
--- a/hald/hald.c
+++ b/hald/hald.c
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
  * Copyright (C) 2005 Danny Kukawka, <danny.kukawka at web.de>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -123,8 +124,33 @@ gdl_store_changed (HalDeviceStore *store
 					    command_line, hal_device_get_udi(device)));
 			}
 		}
+		for (hal_device_property_strlist_iter_init (device, "info.addons.singleton", &iter);
+		     hal_device_property_strlist_iter_is_valid (&iter);
+		     hal_device_property_strlist_iter_next (&iter)) {
+			const gchar *command_line;
+
+			command_line = hal_device_property_strlist_iter_get_value (&iter);
+
+			if (hald_singleton_device_added (command_line, device))
+				hal_device_inc_num_addons (device);
+			else
+				HAL_ERROR(("Couldn't add device to singleton"));
+		}
+
 	} else {
+		HalDeviceStrListIter iter;
+
 		HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device)));
+		for (hal_device_property_strlist_iter_init (device, "info.addons.singleton", &iter);
+		     hal_device_property_strlist_iter_is_valid (&iter);
+		     hal_device_property_strlist_iter_next (&iter)) {
+			const gchar *command_line;
+
+			command_line = hal_device_property_strlist_iter_get_value (&iter);
+
+			hald_singleton_device_removed (command_line, device);
+		}
+
 		hald_runner_kill_device(device);
 	}
 
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index e6377d7..fb37eaf 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -4,6 +4,7 @@
  * dbus.c : D-BUS interface of HAL daemon
  *
  * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -34,7 +35,8 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <sys/time.h>
-
+#include <glib.h>
+#include <glib/gprintf.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
@@ -61,6 +63,7 @@ static CITracker *ci_tracker = NULL;
 #ifdef HAVE_CONKIT
 static CKTracker *ck_tracker = NULL;
 #endif
+static GHashTable *singletons = NULL;
 
 static void
 raise_error (DBusConnection *connection,
@@ -3229,6 +3232,313 @@ addon_is_ready (DBusConnection * connect
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+typedef struct _SingletonInfo SingletonInfo;
+struct _SingletonInfo
+{
+	DBusConnection *connection;
+	GList *devices;
+};
+
+static void
+singleton_info_destroy (SingletonInfo *info)
+{
+	if (info->devices) {
+		HAL_ERROR (("Singleton '%s' exited with devices still to handle"));
+		/*should we signal the devices that the addon's gone?*/
+		g_list_free (info->devices);
+	}
+	g_free(info);
+}
+
+static gboolean
+singleton_remove_by_connection (char *udi,
+			   SingletonInfo *info,
+			   DBusConnection *connection)
+{
+	if (info->connection == connection)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+typedef struct _SingletonDeviceChangedInfo SingletonDeviceChangedInfo;
+struct _SingletonDeviceChangedInfo
+{
+	gboolean added;
+	HalDevice *device;
+};
+
+static SingletonDeviceChangedInfo* new_sdci (gboolean added, HalDevice *device)
+{
+	SingletonDeviceChangedInfo* sdci;
+	sdci = g_new (SingletonDeviceChangedInfo, 1);
+	sdci->added = added;
+	sdci->device = device;
+	g_object_add_weak_pointer (G_OBJECT (device), (gpointer) &(sdci->device));
+	return sdci;
+}
+
+static
+void del_sdci (SingletonDeviceChangedInfo *sdci)
+{
+	if (sdci->device)
+		g_object_remove_weak_pointer (G_OBJECT (sdci->device),
+					      (gpointer)&(sdci->device));
+	g_free (sdci);
+}
+
+static void
+reply_from_singleton_device_changed (DBusPendingCall *pending_call,
+				     void            *user_data);
+
+static void
+singleton_signal_device_changed (DBusConnection *connection, HalDevice *device, gboolean added)
+{
+	DBusError error;
+	DBusMessage *message;
+	DBusMessageIter iter, iter_dict;
+	DBusPendingCall *pending_call;
+	const char *udi = hal_device_get_udi (device);
+
+	message = dbus_message_new_method_call (NULL,
+		"/org/freedesktop/Hal/SingletonAddon",
+		"org.freedesktop.Hal.SingletonAddon",
+		added ? "DeviceAdded" : "DeviceRemoved");
+
+	if (message == NULL)
+		DIE (("No memory"));
+	dbus_message_iter_init_append (message, &iter);
+
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING,
+					&udi);
+
+	dbus_message_iter_open_container (&iter,
+					  DBUS_TYPE_ARRAY,
+					  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					  DBUS_TYPE_STRING_AS_STRING
+					  DBUS_TYPE_VARIANT_AS_STRING
+					  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					  &iter_dict);
+
+	hal_device_property_foreach (device,
+				     foreach_property_append,
+				     &iter_dict);
+
+	dbus_message_iter_close_container (&iter, &iter_dict);
+
+	HAL_DEBUG (("%s about to send message to connection %p", G_STRFUNC, connection));
+	dbus_error_init (&error);
+
+	if (dbus_connection_send_with_reply (connection,
+					     message,
+					     &pending_call,
+					     /*-1*/ 8000)) {
+		/*HAL_INFO (("connection=%x message=%x", connection, message));*/
+		dbus_pending_call_set_notify (pending_call,
+					      reply_from_singleton_device_changed,
+					      (void *) new_sdci (added, device),
+					      (DBusFreeFunction) del_sdci);
+	}
+
+	dbus_message_unref (message);
+}
+
+static void
+reply_from_singleton_device_changed (DBusPendingCall *pending_call,
+				     void            *user_data)
+{
+	DBusMessage *reply;
+	DBusError error;
+	SingletonDeviceChangedInfo *data = (SingletonDeviceChangedInfo*) user_data;
+
+	reply = dbus_pending_call_steal_reply (pending_call);
+
+	if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		dbus_set_error_from_message (&error, reply);
+
+		HAL_ERROR (("Error calling %s for %s on singleton: %s: %s",
+		      data->added ? "DeviceAdded" : "DeviceRemoved",
+		      data->device ? hal_device_get_udi (data->device)
+		                   : "a device that has been deleted",
+		      error.name, error.message));
+
+		dbus_error_free (&error);
+		goto out;
+	}
+
+	/* if the device has been destroyed, data->device will be null */
+	if (data->added && data->device) {
+		HAL_DEBUG (("incrementing ready addons for device"));
+		if (hal_device_inc_num_ready_addons (data->device)) {
+			if (hal_device_are_all_addons_ready (data->device)) {
+				manager_send_signal_device_added (data->device);
+			}
+		}
+	}
+
+out:
+	dbus_message_unref (reply);
+	dbus_pending_call_unref (pending_call);
+}
+
+/**
+ * hald_singleton_device_added:
+ * @command_line: command line identifying addon singleton
+ * @device: device being added
+ *
+ * Signal to a singleton addon that a device has been added
+ *
+ * Return: TRUE if successful
+ */
+gboolean
+hald_singleton_device_added (const char * command_line,
+			     HalDevice *device)
+{
+	SingletonInfo *info;
+	gchar *extra_env[2] = {NULL, NULL};
+
+	if (!singletons)
+		singletons = g_hash_table_new_full (
+				g_str_hash, g_str_equal,
+				g_free,
+				(GDestroyNotify) singleton_info_destroy);
+	info = g_hash_table_lookup (singletons, command_line);
+
+	if (!info) {
+		extra_env[0] = g_malloc (strlen (command_line) + 26);
+		g_sprintf (extra_env[0], "SINGLETON_COMMAND_LINE=%s", command_line);
+		if (hald_runner_start_singleton (command_line, extra_env, NULL, NULL, NULL)) {
+			HAL_INFO (("Started singleton addon %s for udi %s",
+				   command_line, hal_device_get_udi(device)));
+		} else {
+			HAL_ERROR (("Cannot start singleton addon %s for udi %s",
+				    command_line, hal_device_get_udi(device)));
+			return FALSE;
+		}
+		g_free (extra_env[0]);
+		info = g_new0 (SingletonInfo, 1);
+		g_hash_table_insert (singletons, g_strdup(command_line), info);
+	}
+
+	info->devices = g_list_prepend (info->devices, device);
+
+	if (info->connection) {
+		singleton_signal_device_changed (info->connection, device, TRUE);
+	} else {
+		HAL_DEBUG (("singleton not initialised yet"));
+	}
+
+        return TRUE;
+}
+
+/**
+ * hald_singleton_device_removed:
+ * @command_line: command line identifying addon singleton
+ * @device: device that is being removed
+ *
+ * Signal to a singleton addon that a device has been removed
+ *
+ * Return: TRUE if successful
+ */
+gboolean
+hald_singleton_device_removed (const char * command_line,
+			       HalDevice *device)
+{
+	SingletonInfo *info;
+	GList *lp;
+
+	if (G_UNLIKELY (!singletons)) {
+		HAL_ERROR (("Singleton table is not initialied"));
+		return FALSE;
+	}
+
+	info = g_hash_table_lookup (singletons, command_line);
+
+	if (G_UNLIKELY (!info)) {
+		HAL_WARNING (("Attempting to remove a device from an unknown singleton %s", command_line));
+		return FALSE;
+	}
+
+	lp = g_list_find (info->devices, device);
+	if (G_UNLIKELY (!lp)) {
+		HAL_WARNING (("Device was not in use by this singleton: %s", command_line));
+		return FALSE;
+	}
+
+	info->devices = g_list_remove_link (info->devices, lp);
+	g_list_free1 (lp);
+
+	if (G_LIKELY (info->connection)) {
+		singleton_signal_device_changed (info->connection, device, FALSE);
+	}
+
+	return TRUE;
+}
+
+static DBusHandlerResult
+singleton_addon_is_ready (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
+{
+	HalDevice *device;
+	SingletonInfo *info;
+	DBusMessage *reply;
+	DBusError error;
+	const char *command_line;
+	GList *lp;
+
+	HAL_TRACE (("entering"));
+	HAL_DEBUG (("singleton_addon_is_ready"));
+
+	if (!local_interface) {
+		raise_permission_denied (connection, message, "SingletonAddonIsReady: only allowed for helpers");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	dbus_error_init (&error);
+	if (!dbus_message_get_args (message, &error,
+				    DBUS_TYPE_STRING, &command_line,
+				    DBUS_TYPE_INVALID)) {
+		raise_syntax (connection, message, "SingletonAddonIsReady");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	if (G_UNLIKELY (!singletons)) {
+		HAL_ERROR (("Got SingletonIsReady with no known singletons"));
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	info = g_hash_table_lookup (singletons, command_line);
+
+	if (G_UNLIKELY (!info)) {
+		HAL_ERROR (("Got SingletonIsReady from unknown singleton"));
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	HAL_INFO (("SingletonAddonIsReady recieved for '%s'", command_line));
+
+	reply = dbus_message_new_method_return (message);
+	if (reply == NULL)
+		DIE (("No memory"));
+
+	if (!dbus_connection_send (connection, reply, NULL))
+		DIE (("No memory"));
+
+	dbus_message_unref (reply);
+
+	info->connection = connection;
+
+	HAL_DEBUG (("Signalling device added for queued devices"));
+	for (lp = info->devices; lp; lp = lp->next) {
+		device = lp->data;
+
+		HAL_DEBUG (("device added for '%s'", hal_device_get_udi (device)));
+		singleton_signal_device_changed (connection, device, TRUE);
+
+	}
+
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 
 /*
  * Create new device in tdl. Return temporary udi.
@@ -4026,7 +4336,9 @@ do_introspect (DBusConnection  *connecti
 				       "    <method name=\"ReleaseGlobalInterfaceLock\">\n"
 				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
 				       "    </method>\n"
-
+				       "    <method name=\"SingletonAddonIsReady\">\n"
+				       "      <arg name=\"command_line\" direction=\"in\" type=\"s\"/>\n"
+				       "    </method>\n"
 				       "    <signal name=\"DeviceAdded\">\n"
 				       "      <arg name=\"udi\" type=\"s\"/>\n"
 				       "    </signal>\n"
@@ -4433,7 +4745,13 @@ hald_dbus_filter_handle_methods (DBusCon
 						"ReleaseGlobalInterfaceLock") &&
                    strcmp (dbus_message_get_path (message), "/org/freedesktop/Hal/Manager") == 0) {
 		return device_release_global_interface_lock (connection, message, local_interface);
-                
+	} else if (dbus_message_is_method_call (message,
+						"org.freedesktop.Hal.Manager",
+						"SingletonAddonIsReady") &&
+		   strcmp (dbus_message_get_path (message),
+			    "/org/freedesktop/Hal/Manager") == 0) {
+		return singleton_addon_is_ready (connection, message, local_interface);
+
 	} else if (dbus_message_is_method_call (message,
 						"org.freedesktop.Hal.Device",
 						"AcquireInterfaceLock")) {
@@ -4818,7 +5136,6 @@ out:
 }
 
 
-
 static DBusHandlerResult 
 local_server_message_handler (DBusConnection *connection, 
 			      DBusMessage *message, 
@@ -4864,6 +5181,8 @@ local_server_message_handler (DBusConnec
 			}
 		}
 
+		g_hash_table_foreach_remove (singletons, (GHRFunc) singleton_remove_by_connection, connection);
+
 		dbus_connection_unref (connection);
 		return DBUS_HANDLER_RESULT_HANDLED;
 	} else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) {
diff --git a/hald/hald_dbus.h b/hald/hald_dbus.h
index 4ec9239..40187a6 100644
--- a/hald/hald_dbus.h
+++ b/hald/hald_dbus.h
@@ -115,6 +115,9 @@ char *hald_dbus_local_server_addr (void)
 
 gboolean device_is_executing_method (HalDevice *d, const char *interface_name, const char *method_name);
 
+
+gboolean hald_singleton_device_added (const char * commandline, HalDevice *device);
+gboolean hald_singleton_device_removed (const char * commandline, HalDevice *device);
 #ifdef HAVE_CONKIT
 #include "ck-tracker.h"
 
diff-tree 664c3a4023adb99d2fce053ce4d706834bb74d01 (from 1ea4c66304e52b697bdd308ab32bb766ad786bc7)
Author: Rob Taylor <rob.taylor at codethink.co.uk>
Date:   Thu Jun 7 11:52:55 2007 +0100

    [PATCH] add hald-runner support for singleton addons
    
    Introduces a new method StartSingleton on org.freedesktop.HalRunner.  This
    takes one parameter of an array of strings for the environment to launch the
    singleton. hald-runner keeps a list of running singletons, which are killable
    only by KillAll.
    
    The patch also adds hald_runner_start_singleton for starting a singleton.

diff --git a/hald-runner/main.c b/hald-runner/main.c
index 3b170b3..f767b9b 100644
--- a/hald-runner/main.c
+++ b/hald-runner/main.c
@@ -4,6 +4,7 @@
  * main.c - Main dbus interface of the hald runner
  *
  * Copyright (C) 2006 Sjoerd Simons, <sjoerd at luon.net>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -36,19 +37,33 @@
 #endif
 
 static gboolean
-parse_first_part(run_request *r, DBusMessage *msg, DBusMessageIter *iter)
+parse_udi (run_request *r, DBusMessage *msg, DBusMessageIter *iter)
 {
-	DBusMessageIter sub_iter;
 	char *tmpstr;
 
-	/* First should be the device UDI */
+	/* Should be the device UDI */
 	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) 
 		goto malformed;
 	dbus_message_iter_get_basic(iter, &tmpstr);
 	r->udi = g_strdup(tmpstr);
 
-	/* Then the environment array */
-	if (!dbus_message_iter_next(iter) || dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+	if (!dbus_message_iter_next(iter))
+		goto malformed;
+
+	return TRUE;
+
+malformed:
+	return FALSE;
+}
+
+static gboolean
+parse_environment(run_request *r, DBusMessage *msg, DBusMessageIter *iter)
+{
+	DBusMessageIter sub_iter;
+	char *tmpstr;
+
+	/* The environment array */
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
 		goto malformed;
 	dbus_message_iter_recurse(iter, &sub_iter);
 	/* Add default path for the programs we start */
@@ -82,7 +97,10 @@ handle_run(DBusConnection *con, DBusMess
 	r = new_run_request();
 	g_assert(dbus_message_iter_init(msg, &iter));
 
-	if (!parse_first_part(r, msg, &iter)) 
+	if (!parse_udi(r, msg, &iter))
+		goto malformed;
+
+	if (!parse_environment(r, msg, &iter))
 		goto malformed;
 
 	/* Next a string of what should be written to stdin */
@@ -114,7 +132,7 @@ malformed:
 }
 
 static void
-handle_start(DBusConnection *con, DBusMessage *msg)
+handle_start(DBusConnection *con, DBusMessage *msg, gboolean is_singleton)
 {
 	DBusMessage *reply;
 	DBusMessageIter iter;
@@ -122,10 +140,22 @@ handle_start(DBusConnection *con, DBusMe
 	GPid pid __attribute__ ((aligned));
 
 	r = new_run_request();
+	r->is_singleton = is_singleton;
+
 	g_assert(dbus_message_iter_init(msg, &iter));
 
-	if (!dbus_message_iter_init(msg, &iter) || !parse_first_part(r, msg, &iter))
+	if (!dbus_message_iter_init(msg, &iter))
+		goto malformed;
+
+	if (!is_singleton && !parse_udi(r, msg, &iter)) {
+		fprintf(stderr, "error parsing udi");
+		goto malformed;
+	}
+
+	if (!parse_environment(r, msg, &iter)) {
+		fprintf(stderr, "error parsing environment");
 		goto malformed;
+	}
 
 	if (run_request_run(r, con, NULL, &pid)) {
 		reply = dbus_message_new_method_return(msg);
@@ -183,7 +213,10 @@ filter(DBusConnection *con, DBusMessage 
 		handle_run(con, msg);
 		return DBUS_HANDLER_RESULT_HANDLED;
 	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "Start")) {
-		handle_start(con, msg);
+		handle_start(con, msg, FALSE);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "StartSingleton")) {
+		handle_start(con, msg, TRUE);
 		return DBUS_HANDLER_RESULT_HANDLED;
 	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "Kill")) {
 		handle_kill(con, msg);
diff --git a/hald-runner/runner.c b/hald-runner/runner.c
index 57a2a80..cd6cf82 100644
--- a/hald-runner/runner.c
+++ b/hald-runner/runner.c
@@ -4,6 +4,7 @@
  * runner.c - Process running code
  *
  * Copyright (C) 2006 Sjoerd Simons, <sjoerd at luon.net>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -48,6 +49,7 @@
 #define HALD_RUN_KILLED 0x4
 
 GHashTable *udi_hash = NULL;
+GList *singletons = NULL;
 
 typedef struct {
 	run_request *r;
@@ -128,15 +130,19 @@ send_reply(DBusConnection *con, DBusMess
 }
 
 static void
-remove_from_hash_table(run_data *rd)
+remove_run_data(run_data *rd)
 {
 	GList *list;
 
-	/* Remove to the hashtable */
-	list = (GList *)g_hash_table_lookup(udi_hash, rd->r->udi);
-	list = g_list_remove(list, rd);
-	/* The hash table will take care to not leak the dupped string */
-	g_hash_table_insert(udi_hash, g_strdup(rd->r->udi), list);
+	if (rd->r->is_singleton) {
+		singletons = g_list_remove(singletons, rd);
+	} else {
+		/* Remove to the hashtable */
+		list = (GList *)g_hash_table_lookup(udi_hash, rd->r->udi);
+		list = g_list_remove(list, rd);
+		/* The hash table will take care to not leak the dupped string */
+		g_hash_table_insert(udi_hash, g_strdup(rd->r->udi), list);
+	}
 }
 
 static void
@@ -170,7 +176,7 @@ run_exited(GPid pid, gint status, gpoint
 	free_string_array(error);
 
 out:
-	remove_from_hash_table(rd);
+	remove_run_data (rd);
 		
 	/* emit a signal that this PID exited */
 	if(rd->con != NULL && rd->emit_pid_exited) {
@@ -200,7 +206,7 @@ run_timedout(gpointer data) {
 	rd->sent_kill = TRUE;
 
 	send_reply(rd->con, rd->msg, HALD_RUN_TIMEOUT, 0, NULL);
-	remove_from_hash_table(rd);
+	remove_run_data (rd);
 	return FALSE;
 }
 
@@ -300,12 +306,16 @@ run_request_run (run_request *r, DBusCon
 	else
 		rd->timeout = 0;
 
-	/* Add to the hashtable */
-	list = (GList *)g_hash_table_lookup(udi_hash, r->udi);
-	list = g_list_prepend(list, rd);
+	if (r->is_singleton) {
+		singletons = g_list_prepend(singletons, rd);
+	} else {
+		/* Add to the hashtable */
+		list = (GList *)g_hash_table_lookup(udi_hash, r->udi);
+		list = g_list_prepend(list, rd);
 
-	/* The hash table will take care to not leak the dupped string */
-	g_hash_table_insert(udi_hash, g_strdup(r->udi), list);
+		/* The hash table will take care to not leak the dupped string */
+		g_hash_table_insert(udi_hash, g_strdup(r->udi), list);
+	}
 
 	/* send back PID if requested.. and only emit StartedProcessExited in this case */
 	if (out_pid != NULL) {
@@ -363,6 +373,7 @@ void 
 run_kill_all()
 {
 	g_hash_table_foreach_remove(udi_hash, hash_kill_udi, NULL);
+	g_list_foreach(singletons, kill_rd, NULL);
 }
 
 void
diff --git a/hald-runner/runner.h b/hald-runner/runner.h
index 2a18f94..c053381 100644
--- a/hald-runner/runner.h
+++ b/hald-runner/runner.h
@@ -36,6 +36,7 @@ typedef struct {
 	gchar **argv;
 	gchar *input;
 	gboolean error_on_stderr;
+	gboolean is_singleton;
 	guint32 timeout;
 } run_request;
 
diff --git a/hald/hald_runner.c b/hald/hald_runner.c
index 8fd5884..db708e4 100644
--- a/hald/hald_runner.c
+++ b/hald/hald_runner.c
@@ -4,6 +4,7 @@
  * hald_runner.c - Interface to the hal runner helper daemon
  *
  * Copyright (C) 2006 Sjoerd Simons, <sjoerd at luon.net>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -65,6 +66,7 @@ typedef struct {
 	GPid pid;
 	HalDevice *device;
 	HalRunTerminatedCB cb;
+	gboolean is_singleton;
 	gpointer data1;
 	gpointer data2;
 } RunningProcess;
@@ -366,7 +368,8 @@ add_basic_env (DBusMessageIter * iter, c
 	if (hald_use_syslog) {
 		add_env (iter, "HALD_USE_SYSLOG", "1");
 	}
-	add_env (iter, "UDI", udi);
+	if (udi)
+		add_env (iter, "UDI", udi);
 	add_env (iter, "HALD_DIRECT_ADDR", hald_dbus_local_server_addr ());
 
         /* I'm sure it would be easy to remove use of getenv(3) to add these variables... */
@@ -559,11 +562,9 @@ add_command (DBusMessageIter * iter, con
 	return TRUE;
 }
 
-static gboolean
-add_first_part (DBusMessageIter * iter, HalDevice * device,
-		const gchar * command_line, char **extra_env)
+static void
+add_udi (DBusMessageIter * iter, HalDevice * device)
 {
-	DBusMessageIter array_iter;
 	const char *udi;
 
 	if (device != NULL)
@@ -572,7 +573,13 @@ add_first_part (DBusMessageIter * iter, 
 		udi = "";
 
 	dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &udi);
+}
 
+static gboolean
+add_environment (DBusMessageIter * iter, HalDevice * device,
+		const gchar * command_line, char **extra_env)
+{
+	DBusMessageIter array_iter;
 	dbus_message_iter_open_container (iter,
 					  DBUS_TYPE_ARRAY,
 					  DBUS_TYPE_STRING_AS_STRING,
@@ -580,7 +587,7 @@ add_first_part (DBusMessageIter * iter, 
 	if (device != NULL)
 		hal_device_property_foreach (device, add_property_to_msg,
 					     &array_iter);
-	add_basic_env (&array_iter, udi);
+	add_basic_env (&array_iter, device ? hal_device_get_udi (device): NULL);
 	add_extra_env (&array_iter, extra_env);
 	dbus_message_iter_close_container (iter, &array_iter);
 
@@ -591,31 +598,37 @@ add_first_part (DBusMessageIter * iter, 
 }
 
 /* Start a helper, returns true on a successfull start */
-gboolean
-hald_runner_start (HalDevice * device, const gchar * command_line,
-		   char **extra_env, HalRunTerminatedCB cb, gpointer data1,
-		   gpointer data2)
+static gboolean
+runner_start (HalDevice * device, const gchar * command_line,
+	      char **extra_env, gboolean singleton,
+	      HalRunTerminatedCB cb, gpointer data1, gpointer data2)
 {
 	DBusMessage *msg, *reply;
-	DBusError err;
+	DBusError error;
 	DBusMessageIter iter;
 
-	dbus_error_init (&err);
+	dbus_error_init (&error);
 	msg = dbus_message_new_method_call ("org.freedesktop.HalRunner",
 					    "/org/freedesktop/HalRunner",
 					    "org.freedesktop.HalRunner",
-					    "Start");
+					    singleton ? "StartSingleton" : "Start");
 	if (msg == NULL)
 		DIE (("No memory"));
 	dbus_message_iter_init_append (msg, &iter);
 
-	if (!add_first_part (&iter, device, command_line, extra_env))
+	if (!singleton)
+		add_udi (&iter, device);
+
+	if (!add_environment (&iter, device, command_line, extra_env)) {
+		HAL_ERROR (("Error adding environment, device =%p, singleton=%d",
+			device, singleton));
 		goto error;
+	}
 
 	/* Wait for the reply, should be almost instantanious */
 	reply =
 	    dbus_connection_send_with_reply_and_block (runner_connection,
-						       msg, -1, &err);
+						       msg, -1, &error);
 	if (reply) {
 		gboolean ret =
 		    (dbus_message_get_type (reply) ==
@@ -623,7 +636,7 @@ hald_runner_start (HalDevice * device, c
 
 		if (ret) {
 			dbus_int64_t pid_from_runner;
-			if (dbus_message_get_args (reply, &err,
+			if (dbus_message_get_args (reply, &error,
 						   DBUS_TYPE_INT64,
 						   &pid_from_runner,
 						   DBUS_TYPE_INVALID)) {
@@ -632,7 +645,11 @@ hald_runner_start (HalDevice * device, c
 					rp = g_new0 (RunningProcess, 1);
 					rp->pid = (GPid) pid_from_runner;
 					rp->cb = cb;
-					rp->device = device;
+					rp->is_singleton = singleton;
+					if (singleton)
+						rp->device = NULL;
+					else
+						rp->device = device;
 					rp->data1 = data1;
 					rp->data2 = data2;
 
@@ -649,6 +666,10 @@ hald_runner_start (HalDevice * device, c
 		dbus_message_unref (reply);
 		dbus_message_unref (msg);
 		return ret;
+	} else {
+		if (dbus_error_is_set (&error)) {
+			HAL_ERROR (("Error running '%s': %s: %s", command_line, error.name, error.message));
+		}
 	}
 
       error:
@@ -656,6 +677,23 @@ hald_runner_start (HalDevice * device, c
 	return FALSE;
 }
 
+gboolean
+hald_runner_start (HalDevice * device, const gchar * command_line,
+		   char **extra_env, HalRunTerminatedCB cb,
+		   gpointer data1, gpointer data2)
+{
+	return runner_start (device, command_line, extra_env, FALSE, cb, data1, data2);
+}
+
+gboolean
+hald_runner_start_singleton (const gchar * command_line,
+			     char **extra_env, HalRunTerminatedCB cb,
+			     gpointer data1, gpointer data2)
+{
+	return runner_start (NULL, command_line, extra_env, TRUE, cb, data1, data2);
+}
+
+
 static void
 process_reply (DBusMessage *m, HelperData *hb)
 {
@@ -759,7 +797,8 @@ hald_runner_run_method (HalDevice * devi
 		DIE (("No memory"));
 	dbus_message_iter_init_append (msg, &iter);
 
-	if (!add_first_part (&iter, device, command_line, extra_env))
+	add_udi (&iter, device);
+	if (!add_environment (&iter, device, command_line, extra_env))
 		goto error;
 
 	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &input);
@@ -828,7 +867,8 @@ hald_runner_run_sync (HalDevice * device
 		DIE (("No memory"));
 	dbus_message_iter_init_append (msg, &iter);
 
-	if (!add_first_part (&iter, device, command_line, extra_env))
+	add_udi (&iter, device);
+	if (!add_environment (&iter, device, command_line, extra_env))
 		goto error;
 
 	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &input);
diff --git a/hald/hald_runner.h b/hald/hald_runner.h
index c89eeee..0356132 100644
--- a/hald/hald_runner.h
+++ b/hald/hald_runner.h
@@ -5,6 +5,7 @@
  * hald_runner.h - Interface to the hal runner helper daemon
  *
  * Copyright (C) 2006 Sjoerd Simons <sjoerd at luon.net>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -61,6 +62,11 @@ gboolean
 hald_runner_start (HalDevice *device, const gchar *command_line, char **extra_env, 
 		   HalRunTerminatedCB cb, gpointer data1, gpointer data2);
 
+gboolean
+hald_runner_start_singleton (const gchar * command_line,
+			     char **extra_env, HalRunTerminatedCB cb,
+			     gpointer data1, gpointer data2);
+
 /* Run a helper program using the commandline, with input as infomation on
  * stdin */
 void


More information about the hal-commit mailing list