hal: Branch 'master'

David Zeuthen david at kemper.freedesktop.org
Mon Aug 7 20:53:02 PDT 2006


 doc/api/tmpl/blockdev.sgml         |    1 
 doc/api/tmpl/config.sgml           |    7 
 doc/api/tmpl/libhal-storage.sgml   |   27 ++
 doc/api/tmpl/util.sgml             |   25 ++
 hald/hald_dbus.c                   |  250 ++++++++++++++++----
 hald/linux2/probing/probe-volume.c |  149 ++++++------
 libhal/libhal.c                    |  459 +++++++++++++++++++++++++++++++++++++
 libhal/libhal.h                    |   38 ++-
 8 files changed, 837 insertions(+), 119 deletions(-)

New commits:
diff-tree 7b1d143b988b378b3269b767259d387e64b14718 (from 602bbb270d0851047a0bebc442a1fdc92a4f91c7)
Author: David Zeuthen <davidz at redhat.com>
Date:   Mon Aug 7 23:50:47 2006 -0400

    add API to hald (and libhal) to change multiple properties at once
    
    Also converted probe-volume to use this new API.

diff --git a/doc/api/tmpl/blockdev.sgml b/doc/api/tmpl/blockdev.sgml
index 512348f..14ff231 100644
--- a/doc/api/tmpl/blockdev.sgml
+++ b/doc/api/tmpl/blockdev.sgml
@@ -35,7 +35,6 @@ blockdev
 </para>
 
 @sysfs_path: 
- at is_partition: 
 @end_token: 
 
 
diff --git a/doc/api/tmpl/config.sgml b/doc/api/tmpl/config.sgml
index fd0c10c..5a6b893 100644
--- a/doc/api/tmpl/config.sgml
+++ b/doc/api/tmpl/config.sgml
@@ -87,6 +87,13 @@ config
 
 
 
+<!-- ##### MACRO HAVE_ASPRINTF ##### -->
+<para>
+
+</para>
+
+
+
 <!-- ##### MACRO HAVE_BIND_TEXTDOMAIN_CODESET ##### -->
 <para>
 
diff --git a/doc/api/tmpl/libhal-storage.sgml b/doc/api/tmpl/libhal-storage.sgml
index 53a7ecf..ab027a7 100644
--- a/doc/api/tmpl/libhal-storage.sgml
+++ b/doc/api/tmpl/libhal-storage.sgml
@@ -289,6 +289,15 @@ libhal-storage
 @Returns: 
 
 
+<!-- ##### FUNCTION libhal_drive_get_size ##### -->
+<para>
+
+</para>
+
+ at drive: 
+ at Returns: 
+
+
 <!-- ##### FUNCTION libhal_drive_no_partitions_hint ##### -->
 <para>
 
@@ -738,6 +747,24 @@ libhal-storage
 @Returns: 
 
 
+<!-- ##### FUNCTION libhal_volume_get_partition_start_offset ##### -->
+<para>
+
+</para>
+
+ at volume: 
+ at Returns: 
+
+
+<!-- ##### FUNCTION libhal_volume_get_partition_media_size ##### -->
+<para>
+
+</para>
+
+ at volume: 
+ at Returns: 
+
+
 <!-- ##### FUNCTION libhal_volume_get_label ##### -->
 <para>
 
diff --git a/doc/api/tmpl/util.sgml b/doc/api/tmpl/util.sgml
index cd1511b..d738995 100644
--- a/doc/api/tmpl/util.sgml
+++ b/doc/api/tmpl/util.sgml
@@ -126,6 +126,31 @@ util
 @Returns: 
 
 
+<!-- ##### FUNCTION hal_util_get_uint64_from_file ##### -->
+<para>
+
+</para>
+
+ at directory: 
+ at file: 
+ at result: 
+ at base: 
+ at Returns: 
+
+
+<!-- ##### FUNCTION hal_util_set_uint64_from_file ##### -->
+<para>
+
+</para>
+
+ at d: 
+ at key: 
+ at directory: 
+ at file: 
+ at base: 
+ at Returns: 
+
+
 <!-- ##### FUNCTION hal_util_get_string_from_file ##### -->
 <para>
 
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 3f2eab7..2526554 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -904,6 +904,197 @@ device_get_all_properties (DBusConnectio
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static dbus_bool_t 
+sender_has_privileges (DBusConnection *connection, DBusMessage *message)
+{
+	DBusError error;
+	unsigned long user_uid;
+	const char *user_base_svc;
+	dbus_bool_t ret;
+
+	ret = FALSE;
+
+	user_base_svc = dbus_message_get_sender (message);
+	if (user_base_svc == NULL) {
+		HAL_WARNING (("Cannot determine base service of caller"));
+		goto out;
+	}
+
+	HAL_DEBUG (("base_svc = %s", user_base_svc));
+
+	dbus_error_init (&error);
+	user_uid = dbus_bus_get_unix_user (connection, user_base_svc, &error);
+	if (user_uid == (unsigned long) -1 || dbus_error_is_set (&error)) {
+		HAL_WARNING (("Could not get uid for connection: %s %s", error.name, error.message));
+		dbus_error_free (&error);
+		goto out;
+	}
+
+	HAL_INFO (("uid for caller is %ld", user_uid));
+
+	if (user_uid != 0 && user_uid != geteuid()) {
+		HAL_WARNING (("uid %d is not privileged", user_uid));
+		goto out;
+	}
+
+	ret = TRUE;
+
+out:
+	return ret;
+}
+
+
+/** Set multiple properties on a device in an atomic fashion.
+ *
+ *  <pre>
+ *  Device.GetAllProperties(map{string, any} properties)
+ *
+ *    raises org.freedesktop.Hal.NoSuchDevice
+ *  </pre>
+ *
+ *  @param  connection          D-BUS connection
+ *  @param  message             Message
+ *  @return                     What to do with the message
+ */
+static DBusHandlerResult
+device_set_multiple_properties (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict_iter;
+	HalDevice *d;
+	const char *udi;
+
+	udi = dbus_message_get_path (message);
+
+	HAL_TRACE (("entering, udi=%s", udi));
+
+	d = hal_device_store_find (hald_get_gdl (), udi);
+	if (d == NULL)
+		d = hal_device_store_find (hald_get_tdl (), udi);
+
+	if (d == NULL) {
+		raise_no_such_device (connection, message, udi);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	if (!local_interface && !sender_has_privileges (connection, message)) {
+		raise_permission_denied (connection, message, "SetProperty: not privileged");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	dbus_message_iter_init (message, &iter);
+
+	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY  &&
+	    dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DICT_ENTRY) {
+		HAL_ERROR (("error, expecting an array of dict entries", __FILE__, __LINE__));
+		raise_syntax (connection, message, udi);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	dbus_message_iter_recurse (&iter, &dict_iter);
+
+	/* update atomically */
+	device_property_atomic_update_begin ();
+
+	while (dbus_message_iter_get_arg_type (&dict_iter) == DBUS_TYPE_DICT_ENTRY)
+	{
+		DBusMessageIter dict_entry_iter, var_iter, array_iter;
+		const char *key;
+		int change_type;
+		dbus_bool_t rc;
+
+		dbus_message_iter_recurse (&dict_iter, &dict_entry_iter);
+		dbus_message_iter_get_basic (&dict_entry_iter, &key);
+
+		dbus_message_iter_next (&dict_entry_iter);
+		dbus_message_iter_recurse (&dict_entry_iter, &var_iter);
+		change_type = dbus_message_iter_get_arg_type (&var_iter);
+
+		rc = FALSE;
+
+		switch (change_type) {
+		case DBUS_TYPE_ARRAY:
+			if (dbus_message_iter_get_element_type (&var_iter) != DBUS_TYPE_STRING) {
+				/* TODO: error */
+			}
+			dbus_message_iter_recurse (&var_iter, &array_iter);
+
+			hal_device_property_strlist_clear (d, key);
+
+			while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRING) {
+				const char *v;
+				dbus_message_iter_get_basic (&array_iter, &v);
+				HAL_INFO ((" strlist elem %s -> %s", key, v));
+				rc = hal_device_property_strlist_append (d, key, v);
+				dbus_message_iter_next (&array_iter);
+			}
+
+			break;
+		case DBUS_TYPE_STRING:
+		{
+			const char *v;
+			dbus_message_iter_get_basic (&var_iter, &v);
+			HAL_INFO (("%s -> %s", key, v));
+			rc = hal_device_property_set_string (d, key, v);
+			break;
+		}
+		case DBUS_TYPE_INT32:
+		{
+			dbus_int32_t v;			
+			dbus_message_iter_get_basic (&var_iter, &v);
+			HAL_INFO (("%s -> %d", key, v));
+			rc = hal_device_property_set_int (d, key, v);
+			break;
+		}
+		case DBUS_TYPE_UINT64:
+		{
+			dbus_uint64_t v;
+			dbus_message_iter_get_basic (&var_iter, &v);
+			HAL_INFO (("%s -> %lld", key, v));
+			rc = hal_device_property_set_uint64 (d, key, v);
+			break;
+		}
+		case DBUS_TYPE_DOUBLE:
+		{
+			double v;
+			dbus_message_iter_get_basic (&var_iter, &v);
+			HAL_INFO (("%s -> %g", key, v));
+			rc = hal_device_property_set_double (d, key, v);
+			break;
+		}
+		case DBUS_TYPE_BOOLEAN:
+		{
+			gboolean v;
+			dbus_message_iter_get_basic (&var_iter, &v);
+			HAL_INFO (("%s -> %s", key, v ? "True" : "False"));
+			rc = hal_device_property_set_bool (d, key, v);
+			break;
+		}
+		default:
+			/* TODO: error */
+			break;
+		}
+
+		/* TODO: error out on rc==FALSE? */
+
+		dbus_message_iter_next (&dict_iter);
+	}
+
+	device_property_atomic_update_end ();
+
+
+	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);
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 
 /** Get a property on a device.
  *
@@ -1105,46 +1296,6 @@ device_get_property_type (DBusConnection
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-
-static dbus_bool_t 
-sender_has_privileges (DBusConnection *connection, DBusMessage *message)
-{
-	DBusError error;
-	unsigned long user_uid;
-	const char *user_base_svc;
-	dbus_bool_t ret;
-
-	ret = FALSE;
-
-	user_base_svc = dbus_message_get_sender (message);
-	if (user_base_svc == NULL) {
-		HAL_WARNING (("Cannot determine base service of caller"));
-		goto out;
-	}
-
-	HAL_DEBUG (("base_svc = %s", user_base_svc));
-
-	dbus_error_init (&error);
-	user_uid = dbus_bus_get_unix_user (connection, user_base_svc, &error);
-	if (user_uid == (unsigned long) -1 || dbus_error_is_set (&error)) {
-		HAL_WARNING (("Could not get uid for connection: %s %s", error.name, error.message));
-		dbus_error_free (&error);
-		goto out;
-	}
-
-	HAL_INFO (("uid for caller is %ld", user_uid));
-
-	if (user_uid != 0 && user_uid != geteuid()) {
-		HAL_WARNING (("uid %d is not privileged", user_uid));
-		goto out;
-	}
-
-	ret = TRUE;
-
-out:
-	return ret;
-}
-
 /** Set a property on a device.
  *
  *  <pre>
@@ -3044,6 +3195,9 @@ do_introspect (DBusConnection  *connecti
 				       "    <method name=\"GetAllProperties\">\n"
 				       "      <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n"
 				       "    </method>\n"
+				       "    <method name=\"SetMultipleProperties\">\n"
+				       "      <arg name=\"properties\" direction=\"in\" type=\"a{sv}\"/>\n"
+				       "    </method>\n"
 				       "    <method name=\"GetProperty\">\n"
 				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
 				       "      <arg name=\"value\" direction=\"out\" type=\"v\"/>\n"
@@ -3336,33 +3490,33 @@ hald_dbus_filter_handle_methods (DBusCon
 			      "/org/freedesktop/Hal/Manager") == 0) {
 		return manager_find_device_by_capability (connection,
 							  message);
-	}
-	else if (dbus_message_is_method_call (message,
+	} else if (dbus_message_is_method_call (message,
 						"org.freedesktop.Hal.Manager",
 						"NewDevice") &&
 		   strcmp (dbus_message_get_path (message),
 			    "/org/freedesktop/Hal/Manager") == 0) {
 		return manager_new_device (connection, message, local_interface);
-	}
-	else if (dbus_message_is_method_call (message,
+	} else if (dbus_message_is_method_call (message,
 						"org.freedesktop.Hal.Manager",
 						"Remove") &&
 		   strcmp (dbus_message_get_path (message),
 			    "/org/freedesktop/Hal/Manager") == 0) {
 		return manager_remove (connection, message, local_interface);
-	}
-	else if (dbus_message_is_method_call (message,
+	} else if (dbus_message_is_method_call (message,
 						"org.freedesktop.Hal.Manager",
 						"CommitToGdl") &&
 		   strcmp (dbus_message_get_path (message),
 			    "/org/freedesktop/Hal/Manager") == 0) {
 		return manager_commit_to_gdl (connection, message, local_interface);
-	}
-	else if (dbus_message_is_method_call (message,
+	} else if (dbus_message_is_method_call (message,
 					      "org.freedesktop.Hal.Device",
 					      "GetAllProperties")) {
 		return device_get_all_properties (connection, message);
 	} else if (dbus_message_is_method_call (message,
+					      "org.freedesktop.Hal.Device",
+					      "SetMultipleProperties")) {
+		return device_set_multiple_properties (connection, message, local_interface);
+	} else if (dbus_message_is_method_call (message,
 						"org.freedesktop.Hal.Device",
 						"GetProperty")) {
 		return device_get_property (connection, message);
diff --git a/hald/linux2/probing/probe-volume.c b/hald/linux2/probing/probe-volume.c
index 90304d1..4244652 100644
--- a/hald/linux2/probing/probe-volume.c
+++ b/hald/linux2/probing/probe-volume.c
@@ -223,7 +223,7 @@ static unsigned char *probe_msdos_part_t
 }
 
 static void
-set_volume_id_values (LibHalContext *ctx, const char *udi, struct volume_id *vid)
+set_volume_id_values (LibHalContext *ctx, const char *udi, LibHalChangeSet *changes, struct volume_id *vid)
 {
 	char buf[256];
 	const char *usage;
@@ -246,47 +246,47 @@ set_volume_id_values (LibHalContext *ctx
 		usage = "crypto";
 		break;
 	case VOLUME_ID_UNUSED:
-		libhal_device_set_property_string (ctx, udi, "info.product", "Volume (unused)", &error);
+		libhal_changeset_set_property_string (changes, "info.product", "Volume (unused)");
 		usage = "unused";
 		return;
 	default:
 		usage = "";
 	}
 
-	libhal_device_set_property_string (ctx, udi, "volume.fsusage", usage, &error);
+	libhal_changeset_set_property_string (changes, "volume.fsusage", usage);
 	dbg ("volume.fsusage = '%s'", usage);
 
-	libhal_device_set_property_string (ctx, udi, "volume.fstype", vid->type, &error);
+	libhal_changeset_set_property_string (changes, "volume.fstype", vid->type);
 	dbg ("volume.fstype = '%s'", vid->type);
 	if (vid->type_version[0] != '\0') {
-		libhal_device_set_property_string (ctx, udi, "volume.fsversion", vid->type_version, &error);
+		libhal_changeset_set_property_string (changes, "volume.fsversion", vid->type_version);
 		dbg ("volume.fsversion = '%s'", vid->type_version);
 	}
-	libhal_device_set_property_string (ctx, udi, "volume.uuid", vid->uuid, &error);
+	libhal_changeset_set_property_string (changes, "volume.uuid", vid->uuid);
 	dbg ("volume.uuid = '%s'", vid->uuid);
 
 	/* we need to be sure for a utf8 valid label, because dbus accept only utf8 valid strings */
 	volume_label = strdup_valid_utf8 (vid->label);
 	if( volume_label != NULL ) {
-		libhal_device_set_property_string (ctx, udi, "volume.label", volume_label, &error);
+		libhal_changeset_set_property_string (changes, "volume.label", volume_label);
 		dbg ("volume.label = '%s'", volume_label);
 		
 		if (strlen(volume_label) > 0) {	
-			libhal_device_set_property_string (ctx, udi, "info.product", volume_label, &error);
+			libhal_changeset_set_property_string (changes, "info.product", volume_label);
 		}
 		else {
 			snprintf (buf, sizeof (buf), "Volume (%s)", vid->type);
-			libhal_device_set_property_string (ctx, udi, "info.product", buf, &error);
+			libhal_changeset_set_property_string (changes, "info.product", buf);
 		}
 		g_free(volume_label);
 	} else {
 		snprintf (buf, sizeof (buf), "Volume (%s)", vid->type);
-		libhal_device_set_property_string (ctx, udi, "info.product", buf, &error);
+		libhal_changeset_set_property_string (changes, "info.product", buf);
 	}
 }
 
 static void
-advanced_disc_detect (LibHalContext *ctx, const char *udi,
+advanced_disc_detect (LibHalContext *ctx, const char *udi, LibHalChangeSet *changes,
                       int fd, const char *device_file)
 {
 	/* the discs block size */
@@ -312,9 +312,9 @@ advanced_disc_detect (LibHalContext *ctx
 	dbus_error_init (&error);
 	
 	/* set defaults */
-	libhal_device_set_property_bool (ctx, udi, "volume.disc.is_videodvd", FALSE, &error);
-	libhal_device_set_property_bool (ctx, udi, "volume.disc.is_vcd", FALSE, &error);
-	libhal_device_set_property_bool (ctx, udi, "volume.disc.is_svcd", FALSE, &error);
+	libhal_changeset_set_property_bool (changes, "volume.disc.is_videodvd", FALSE);
+	libhal_changeset_set_property_bool (changes, "volume.disc.is_vcd", FALSE);
+	libhal_changeset_set_property_bool (changes, "volume.disc.is_svcd", FALSE);
 	
 	/* read the block size */
 	lseek (fd, 0x8080, SEEK_CUR);
@@ -384,19 +384,19 @@ advanced_disc_detect (LibHalContext *ctx
 		{
 			if (!strcmp (dirname, "VIDEO_TS"))
 			{
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_videodvd", TRUE, &error);
+				libhal_changeset_set_property_bool (changes, "volume.disc.is_videodvd", TRUE);
 				dbg ("Disc in %s is a Video DVD", device_file);
 				break;
 			}
 			else if (!strcmp (dirname, "VCD"))
 			{
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_vcd", TRUE, &error);
+				libhal_changeset_set_property_bool (changes, "volume.disc.is_vcd", TRUE);
 				dbg ("Disc in %s is a Video CD", device_file);
 				break;
 			}
 			else if (!strcmp (dirname, "SVCD"))
 			{
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_svcd", TRUE, &error);
+				libhal_changeset_set_property_bool (changes, "volume.disc.is_svcd", TRUE);
 				dbg ("Disc in %s is a Super Video CD", device_file);
 				break;
 			}
@@ -441,6 +441,7 @@ main (int argc, char *argv[])
 	dbus_uint64_t vol_size;
 	dbus_bool_t should_probe_for_fs;
 	dbus_uint64_t vol_probe_offset = 0;
+	LibHalChangeSet *changeset;
 	fd = -1;
 
 	/* hook in our debug into libvolume_id */
@@ -475,6 +476,12 @@ main (int argc, char *argv[])
 	if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
 		goto out;
 
+	changeset = libhal_device_new_changeset (udi);
+	if (changeset == NULL) {
+		dbg ("Cannot initialize changeset");
+		goto out;
+	}
+
 	dbg ("Doing probe-volume for %s\n", device_file);
 
 	fd = open (device_file, O_RDONLY);
@@ -484,11 +491,11 @@ main (int argc, char *argv[])
 	/* block size and total size */
 	if (ioctl (fd, BLKSSZGET, &block_size) == 0) {
 		dbg ("volume.block_size = %d", block_size);
-		libhal_device_set_property_int (ctx, udi, "volume.block_size", block_size, &error);
+		libhal_changeset_set_property_int (changeset, "volume.block_size", block_size);
 	}
 	if (ioctl (fd, BLKGETSIZE64, &vol_size) == 0) {
 		dbg ("volume.size = %llu", vol_size);
-		libhal_device_set_property_uint64 (ctx, udi, "volume.size", vol_size, &error);
+		libhal_changeset_set_property_uint64 (changeset, "volume.size", vol_size);
 	} else
 		vol_size = 0;
 
@@ -500,12 +507,12 @@ main (int argc, char *argv[])
 		struct cdrom_tochdr; /* toc_hdr; */
 
 		/* defaults */
-		libhal_device_set_property_string (ctx, udi, "volume.disc.type", "unknown", &error);
-		libhal_device_set_property_bool (ctx, udi, "volume.disc.has_audio", FALSE, &error);
-		libhal_device_set_property_bool (ctx, udi, "volume.disc.has_data", FALSE, &error);
-		libhal_device_set_property_bool (ctx, udi, "volume.disc.is_blank", FALSE, &error);
-		libhal_device_set_property_bool (ctx, udi, "volume.disc.is_appendable", FALSE, &error);
-		libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", FALSE, &error);
+		libhal_changeset_set_property_string (changeset, "volume.disc.type", "unknown");
+		libhal_changeset_set_property_bool (changeset, "volume.disc.has_audio", FALSE);
+		libhal_changeset_set_property_bool (changeset, "volume.disc.has_data", FALSE);
+		libhal_changeset_set_property_bool (changeset, "volume.disc.is_blank", FALSE);
+		libhal_changeset_set_property_bool (changeset, "volume.disc.is_appendable", FALSE);
+		libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", FALSE);
 
 		/* Suggested by Alex Larsson to get rid of log spewage
 		 * on Alan's cd changer (RH bug 130649) */
@@ -517,33 +524,33 @@ main (int argc, char *argv[])
 		type = ioctl (fd, CDROM_DISC_STATUS, CDSL_CURRENT);
 		switch (type) {
 		case CDS_AUDIO:		/* audio CD */
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.has_audio", TRUE, &error);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.has_audio", TRUE);
 			dbg ("Disc in %s has audio", device_file);
 			should_probe_for_fs = FALSE;
 			break;
 		case CDS_MIXED:		/* mixed mode CD */
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.has_audio", TRUE, &error);
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.has_data", TRUE, &error);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.has_audio", TRUE);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.has_data", TRUE);
 			dbg ("Disc in %s has audio+data", device_file);
 			break;
 		case CDS_DATA_1:	/* data CD */
 		case CDS_DATA_2:
 		case CDS_XA_2_1:
 		case CDS_XA_2_2:
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.has_data", TRUE, &error);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.has_data", TRUE);
 			dbg ("Disc in %s has data", device_file);
-			advanced_disc_detect (ctx, udi, fd, device_file);
+			advanced_disc_detect (ctx, udi, changeset, fd, device_file);
 			break;
 		case CDS_NO_INFO:	/* blank or invalid CD */
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.is_blank", TRUE, &error);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.is_blank", TRUE);
 			/* set the volume size to 0 if disc is blank and not as 4 from BLKGETSIZE64 */
-			libhal_device_set_property_int (ctx, udi, "volume.block_size", 0, &error);
+			libhal_changeset_set_property_int (changeset, "volume.block_size", 0);
 			dbg ("Disc in %s is blank", device_file);
 			should_probe_for_fs = FALSE;
 			break;
 			
 		default:		/* should never see this */
-			libhal_device_set_property_string (ctx, udi, "volume.disc_type", "unknown", &error);
+			libhal_changeset_set_property_string (changeset, "volume.disc_type", "unknown");
 			dbg ("Disc in %s returned unknown CDROM_DISC_STATUS", device_file);
 			should_probe_for_fs = FALSE;
 			break;
@@ -557,65 +564,65 @@ main (int argc, char *argv[])
 		if (type != -1) {
 			switch (type) {
 			case 0x08: /* CD-ROM */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "cd_rom", &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "cd_rom");
 				break;
 			case 0x09: /* CD-R */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "cd_r", &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "cd_r");
 				break;
 			case 0x0a: /* CD-RW */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "cd_rw", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "cd_rw");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			case 0x10: /* DVD-ROM */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_rom", &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_rom");
 				break;
 			case 0x11: /* DVD-R Sequential */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_r", &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_r");
 				break;
 			case 0x12: /* DVD-RAM */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_ram", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_ram");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			case 0x13: /* DVD-RW Restricted Overwrite */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_rw", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_rw");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			case 0x14: /* DVD-RW Sequential */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_rw", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_rw");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			case 0x1A: /* DVD+RW */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_plus_rw", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_plus_rw");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			case 0x1B: /* DVD+R */
-				libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_plus_r", &error);
+				libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_plus_r");
 				break;
 			case 0x2B: /* DVD+R Double Layer */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "dvd_plus_r_dl", &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "dvd_plus_r_dl");
 				break;
 			case 0x40: /* BD-ROM  */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "bd_rom", &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "bd_rom");
 				break;
 			case 0x41: /* BD-R Sequential */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "bd_r", &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "bd_r");
 				break;
 			case 0x42: /* BD-R Random */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "bd_r", &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "bd_r");
 				break;
 			case 0x43: /* BD-RE */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "bd_re", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "bd_re");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			case 0x50: /* HD DVD-ROM */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "hddvd_rom", &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "hddvd_rom");
 				break;
 			case 0x51: /* HD DVD-R */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "hddvd_r", &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "hddvd_r");
 				break;
 			case 0x52: /* HD DVD-Rewritable */
-                          	libhal_device_set_property_string (ctx, udi, "volume.disc.type", "hddvd_rw", &error);
-				libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+                          	libhal_changeset_set_property_string (changeset, "volume.disc.type", "hddvd_rw");
+				libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 				break;
 			default: 
 				break;
@@ -624,16 +631,16 @@ main (int argc, char *argv[])
 
 		if (get_disc_capacity_for_type (fd, type, &capacity) == 0) {
 			dbg ("volume.disc.capacity = %llu", capacity);
-			libhal_device_set_property_uint64 (ctx, udi, "volume.disc.capacity", capacity, &error);
+			libhal_changeset_set_property_uint64 (changeset, "volume.disc.capacity", capacity);
 		}
 
 		/* On some hardware the get_disc_type call fails, so we use this as a backup */
 		if (disc_is_rewritable (fd)) {
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.is_rewritable", TRUE, &error);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.is_rewritable", TRUE);
 		}
 		
 		if (disc_is_appendable (fd)) {
-			libhal_device_set_property_bool (ctx, udi, "volume.disc.is_appendable", TRUE, &error);
+			libhal_changeset_set_property_bool (changeset, "volume.disc.is_appendable", TRUE);
 		}
 
 #if 0
@@ -685,9 +692,9 @@ main (int argc, char *argv[])
 		vid = volume_id_open_fd (fd);
 		if (vid != NULL) {
 			if (volume_id_probe_all (vid, vol_probe_offset , vol_size) == 0) {
-				set_volume_id_values(ctx, udi, vid);
+				set_volume_id_values(ctx, udi, changeset, vid);
 			} else {
-				libhal_device_set_property_string (ctx, udi, "info.product", "Volume", &error);
+				libhal_changeset_set_property_string (changeset, "info.product", "Volume");
 			}
 			volume_id_close(vid);
 		}
@@ -709,9 +716,8 @@ main (int argc, char *argv[])
 
 					type = idx[partition_number - 1];
 					if (type > 0) {
-						libhal_device_set_property_int (
-							ctx, udi, "volume.partition.msdos_part_table_type",
-							type, &error);
+						libhal_changeset_set_property_int (
+							changeset, "volume.partition.msdos_part_table_type", type);
 
 						/* NOTE: We trust the type from the partition table
 						 * if it explicitly got correct entries for RAID and
@@ -724,8 +730,8 @@ main (int argc, char *argv[])
 						 * Linux RAID autodetect is 0xfd and Linux LVM is 0x8e
 						 */
 						if (type == 0xfd || type == 0x8e ) {
-							libhal_device_set_property_string (
-								ctx, udi, "volume.fsusage", "raid", &error);
+							libhal_changeset_set_property_string (
+								changeset, "volume.fsusage", "raid");
 						}
 					}
 				}
@@ -738,6 +744,13 @@ main (int argc, char *argv[])
 	/* good so far */
 	ret = 0;
 
+	/* for testing...
+	  char *values[4] = {"foo", "bar", "baz", NULL};
+	  libhal_changeset_set_property_strlist (changeset, "foo.bar", values);
+	*/
+
+	libhal_device_commit_changeset (ctx, changeset, &error);
+	libhal_device_free_changeset (changeset);
 
 out:
 	if (fd >= 0)
diff --git a/libhal/libhal.c b/libhal/libhal.c
index c4c8034..b7eae2e 100644
--- a/libhal/libhal.c
+++ b/libhal/libhal.c
@@ -3450,3 +3450,462 @@ libhal_device_claim_interface (LibHalCon
 
 	return result;	
 }
+
+
+
+struct LibHalChangeSetElement_s;
+
+typedef struct LibHalChangeSetElement_s LibHalChangeSetElement;
+
+struct LibHalChangeSetElement_s {
+	char *key;
+	int change_type;
+	union {
+		char *val_str;
+		dbus_int32_t val_int;
+		dbus_uint64_t val_uint64;
+		double val_double;
+		dbus_bool_t val_bool;
+		char **val_strlist;
+	} value;
+	LibHalChangeSetElement *next;
+};
+
+struct LibHalChangeSet_s {
+	char *udi;
+	LibHalChangeSetElement *head;
+};
+
+/**
+ * libhal_device_new_changeset:
+ * @udi: unique device identifier
+ *
+ * Request a new changeset object. Used for changing multiple properties at once. Useful when
+ * performance is critical and also for atomically updating several properties.
+ * 
+ * Returns: A new changeset object or NULL on error
+ */
+LibHalChangeSet *
+libhal_device_new_changeset (const char *udi)
+{
+	LibHalChangeSet *changeset;
+
+	changeset = calloc (1, sizeof (LibHalChangeSet));
+	if (changeset == NULL)
+		goto out;
+
+	changeset->udi = strdup (udi);
+	if (changeset->udi == NULL) {
+		free (changeset);
+		changeset = NULL;
+		goto out;
+	}
+
+	changeset->head = NULL;
+
+out:
+	return changeset;
+}
+
+/**
+ * libhal_device_set_property_string:
+ * @changeset: the changeset
+ * @key: key of property 
+ * @value: the value to set
+ * 
+ * Set a property.
+ * 
+ * Returns: FALSE on OOM
+ */
+dbus_bool_t
+libhal_changeset_set_property_string (LibHalChangeSet *changeset, const char *key, const char *value)
+{
+	LibHalChangeSetElement *elem;
+
+	elem = calloc (1, sizeof (LibHalChangeSetElement));
+	if (elem == NULL)
+		goto out;
+	elem->key = strdup (key);
+	if (elem->key == NULL) {
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	elem->change_type = LIBHAL_PROPERTY_TYPE_STRING;
+	elem->value.val_str = strdup (value);
+	if (elem->value.val_str == NULL) {
+		free (elem->key);
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	elem->next = changeset->head;
+	changeset->head = elem;
+out:
+	return elem != NULL;
+}
+
+/**
+ * libhal_device_set_property_int:
+ * @changeset: the changeset
+ * @key: key of property 
+ * @value: the value to set
+ * 
+ * Set a property.
+ * 
+ * Returns: FALSE on OOM
+ */
+dbus_bool_t
+libhal_changeset_set_property_int (LibHalChangeSet *changeset, const char *key, dbus_int32_t value)
+{
+	LibHalChangeSetElement *elem;
+
+	elem = calloc (1, sizeof (LibHalChangeSetElement));
+	if (elem == NULL)
+		goto out;
+	elem->key = strdup (key);
+	if (elem->key == NULL) {
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	elem->change_type = LIBHAL_PROPERTY_TYPE_INT32;
+	elem->value.val_int = value;
+
+	elem->next = changeset->head;
+	changeset->head = elem;
+out:
+	return elem != NULL;
+}
+
+/**
+ * libhal_device_set_property_uint64:
+ * @changeset: the changeset
+ * @key: key of property 
+ * @value: the value to set
+ * 
+ * Set a property.
+ * 
+ * Returns: FALSE on OOM
+ */
+dbus_bool_t
+libhal_changeset_set_property_uint64 (LibHalChangeSet *changeset, const char *key, dbus_uint64_t value)
+{
+	LibHalChangeSetElement *elem;
+
+	elem = calloc (1, sizeof (LibHalChangeSetElement));
+	if (elem == NULL)
+		goto out;
+	elem->key = strdup (key);
+	if (elem->key == NULL) {
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	elem->change_type = LIBHAL_PROPERTY_TYPE_UINT64;
+	elem->value.val_uint64 = value;
+
+	elem->next = changeset->head;
+	changeset->head = elem;
+out:
+	return elem != NULL;
+}
+
+/**
+ * libhal_device_set_property_double:
+ * @changeset: the changeset
+ * @key: key of property 
+ * @value: the value to set
+ * 
+ * Set a property.
+ * 
+ * Returns: FALSE on OOM
+ */
+dbus_bool_t
+libhal_changeset_set_property_double (LibHalChangeSet *changeset, const char *key, double value)
+{
+	LibHalChangeSetElement *elem;
+
+	elem = calloc (1, sizeof (LibHalChangeSetElement));
+	if (elem == NULL)
+		goto out;
+	elem->key = strdup (key);
+	if (elem->key == NULL) {
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	elem->change_type = LIBHAL_PROPERTY_TYPE_DOUBLE;
+	elem->value.val_double = value;
+
+	elem->next = changeset->head;
+	changeset->head = elem;
+out:
+	return elem != NULL;
+}
+
+/**
+ * libhal_device_set_property_bool:
+ * @changeset: the changeset
+ * @key: key of property 
+ * @value: the value to set
+ * 
+ * Set a property.
+ * 
+ * Returns: FALSE on OOM
+ */
+dbus_bool_t
+libhal_changeset_set_property_bool (LibHalChangeSet *changeset, const char *key, dbus_bool_t value)
+{
+	LibHalChangeSetElement *elem;
+
+	elem = calloc (1, sizeof (LibHalChangeSetElement));
+	if (elem == NULL)
+		goto out;
+	elem->key = strdup (key);
+	if (elem->key == NULL) {
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	elem->change_type = LIBHAL_PROPERTY_TYPE_BOOLEAN;
+	elem->value.val_bool = value;
+
+	elem->next = changeset->head;
+	changeset->head = elem;
+out:
+	return elem != NULL;
+}
+
+/**
+ * libhal_device_set_property_strlist:
+ * @changeset: the changeset
+ * @key: key of property 
+ * @value: the value to set - NULL terminated array of strings
+ * 
+ * Set a property.
+ * 
+ * Returns: FALSE on OOM
+ */
+dbus_bool_t
+libhal_changeset_set_property_strlist (LibHalChangeSet *changeset, const char *key, const char **value)
+{
+	LibHalChangeSetElement *elem;
+	char **value_copy;
+	int len;
+	int i, j;
+
+	elem = calloc (1, sizeof (LibHalChangeSetElement));
+	if (elem == NULL)
+		goto out;
+	elem->key = strdup (key);
+	if (elem->key == NULL) {
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	for (i = 0; value[i] != NULL; i++)
+		;
+	len = i;
+
+	value_copy = calloc (len + 1, sizeof (char *));
+	if (value_copy == NULL) {
+		free (elem->key);
+		free (elem);
+		elem = NULL;
+		goto out;
+	}
+
+	for (i = 0; i < len; i++) {
+		value_copy[i] = strdup (value[i]);
+		if (value_copy[i] == NULL) {
+			for (j = 0; j < i; j++) {
+				free (value_copy[j]);
+			}
+			free (value_copy);
+			free (elem->key);
+			free (elem);
+			elem = NULL;
+			goto out;
+		}
+	}
+	value_copy[i] = NULL;
+
+	elem->change_type = LIBHAL_PROPERTY_TYPE_STRLIST;
+	elem->value.val_strlist = value_copy;
+
+	elem->next = changeset->head;
+	changeset->head = elem;
+out:
+	return elem != NULL;
+}
+
+/**
+ * libhal_device_commit_changeset:
+ * @ctx: the context for the connection to hald
+ * @changeset: the changeset to commit
+ * @error: pointer to an initialized dbus error object for returning errors or NULL
+ * 
+ * Commit a changeset to the daemon.
+ * 
+ * Returns: True if the changeset was committed on the daemon side
+ */
+dbus_bool_t
+libhal_device_commit_changeset (LibHalContext *ctx, LibHalChangeSet *changeset, DBusError *error)
+{
+	LibHalChangeSetElement *elem;
+	DBusMessage *message;
+	DBusMessage *reply;
+	DBusError _error;
+	DBusMessageIter iter;
+	DBusMessageIter sub;
+	DBusMessageIter sub2;
+	DBusMessageIter sub3;
+	DBusMessageIter sub4;
+	int i;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal", changeset->udi,
+						"org.freedesktop.Hal.Device",
+						"SetMultipleProperties");
+
+	if (message == NULL) {
+		fprintf (stderr, "%s %d : Couldn't allocate D-BUS message\n", __FILE__, __LINE__);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append (message, &iter);
+
+	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,
+					  &sub);
+
+	for (elem = changeset->head; elem != NULL; elem = elem->next) {
+
+		dbus_message_iter_open_container (&sub,
+						  DBUS_TYPE_DICT_ENTRY,
+						  NULL,
+						  &sub2);
+		dbus_message_iter_append_basic (&sub2, DBUS_TYPE_STRING, &(elem->key));
+
+		switch (elem->change_type) {
+		case LIBHAL_PROPERTY_TYPE_STRING:
+			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &sub3);
+			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_STRING, &(elem->value.val_str));
+			dbus_message_iter_close_container (&sub2, &sub3);
+			break;
+		case LIBHAL_PROPERTY_TYPE_STRLIST:
+			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, 
+							  DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, &sub3);
+			dbus_message_iter_open_container (&sub3, DBUS_TYPE_ARRAY, 
+							  DBUS_TYPE_STRING_AS_STRING, &sub4);
+			for (i = 0; elem->value.val_strlist[i] != NULL; i++) {
+				dbus_message_iter_append_basic (&sub4, DBUS_TYPE_STRING, 
+								&(elem->value.val_strlist[i]));
+			}
+			dbus_message_iter_close_container (&sub3, &sub4);
+			dbus_message_iter_close_container (&sub2, &sub3);
+			break;
+		case LIBHAL_PROPERTY_TYPE_INT32:
+			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &sub3);
+			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_INT32, &(elem->value.val_int));
+			dbus_message_iter_close_container (&sub2, &sub3);
+			break;
+		case LIBHAL_PROPERTY_TYPE_UINT64:
+			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT64_AS_STRING, &sub3);
+			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_UINT64, &(elem->value.val_uint64));
+			dbus_message_iter_close_container (&sub2, &sub3);
+			break;
+		case LIBHAL_PROPERTY_TYPE_DOUBLE:
+			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_DOUBLE_AS_STRING, &sub3);
+			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_DOUBLE, &(elem->value.val_double));
+			dbus_message_iter_close_container (&sub2, &sub3);
+			break;
+		case LIBHAL_PROPERTY_TYPE_BOOLEAN:
+			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING,&sub3);
+			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_BOOLEAN, &(elem->value.val_bool));
+			dbus_message_iter_close_container (&sub2, &sub3);
+			break;
+		default:
+			fprintf (stderr, "%s %d : unknown change_type %d\n", __FILE__, __LINE__, elem->change_type);
+			break;
+		}
+		dbus_message_iter_close_container (&sub, &sub2);
+	}
+
+	dbus_message_iter_close_container (&iter, &sub);
+
+	
+	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 FALSE;
+	}
+
+	if (reply == NULL) {
+		dbus_message_unref (message);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/**
+ * libhal_device_free_changeset:
+ * @changeset: the changeset to free
+ * 
+ * Free a changeset.
+ */
+void
+libhal_device_free_changeset (LibHalChangeSet *changeset)
+{
+	LibHalChangeSetElement *elem;
+	LibHalChangeSetElement *elem2;
+
+	for (elem = changeset->head; elem != NULL; elem = elem2) {
+		elem2 = elem->next;
+
+		switch (elem->change_type) {
+		case LIBHAL_PROPERTY_TYPE_STRING:
+			free (elem->value.val_str);
+			break;
+		case LIBHAL_PROPERTY_TYPE_STRLIST:
+			libhal_free_string_array (elem->value.val_strlist);
+			break;
+                /* explicit fallthrough */
+		case LIBHAL_PROPERTY_TYPE_INT32:
+		case LIBHAL_PROPERTY_TYPE_UINT64:
+		case LIBHAL_PROPERTY_TYPE_DOUBLE:
+		case LIBHAL_PROPERTY_TYPE_BOOLEAN:
+			break;
+		default:
+			fprintf (stderr, "%s %d : unknown change_type %d\n", __FILE__, __LINE__, elem->change_type);
+			break;
+		}
+		free (elem);
+	}
+
+	free (changeset->udi);
+	free (changeset);
+}
diff --git a/libhal/libhal.h b/libhal/libhal.h
index 11af1d2..15ee2d4 100644
--- a/libhal/libhal.h
+++ b/libhal/libhal.h
@@ -357,14 +357,48 @@ LibHalPropertyType libhal_device_get_pro
 						    const char *key,
 						    DBusError *error);
 
+struct LibHalChangeSet_s;
+typedef struct LibHalChangeSet_s LibHalChangeSet;
+
+LibHalChangeSet *libhal_device_new_changeset (const char *udi);
+
+dbus_bool_t libhal_changeset_set_property_string (LibHalChangeSet *changeset,
+						  const char *key,
+						  const char *value);
+
+dbus_bool_t libhal_changeset_set_property_int (LibHalChangeSet *changeset,
+					       const char *key,
+					       dbus_int32_t value);
+
+dbus_bool_t libhal_changeset_set_property_uint64 (LibHalChangeSet *changeset,
+						  const char *key,
+						  dbus_uint64_t value);
+
+dbus_bool_t libhal_changeset_set_property_double (LibHalChangeSet *changeset,
+						  const char *key,
+						  double value);
+
+dbus_bool_t libhal_changeset_set_property_bool (LibHalChangeSet *changeset,
+						const char *key,
+						dbus_bool_t value);
+
+dbus_bool_t libhal_changeset_set_property_strlist (LibHalChangeSet *changeset,
+						   const char *key,
+						   const char **value);
+
+dbus_bool_t libhal_device_commit_changeset (LibHalContext *ctx,
+					    LibHalChangeSet *changeset,
+					    DBusError *error);
+
+void libhal_device_free_changeset (LibHalChangeSet *changeset);
+
 
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
 struct LibHalProperty_s;
 typedef struct LibHalProperty_s LibHalProperty;
 
 struct LibHalPropertySet_s;
 typedef struct LibHalPropertySet_s LibHalPropertySet;
-#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
 
 /* Retrieve all the properties on a device. */
 LibHalPropertySet *libhal_device_get_all_properties (LibHalContext *ctx, 



More information about the hal-commit mailing list