summary refs log tree commit diff
path: root/libnm/nm-object.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
committerMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
commit494f296a3baab08522617b24b1f126d8f9a17502 (patch)
treec8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /libnm/nm-object.c
parent54f6333410ffd570e62717d9e77c5c987175e397 (diff)
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'libnm/nm-object.c')
-rw-r--r--libnm/nm-object.c201
1 files changed, 134 insertions, 67 deletions
diff --git a/libnm/nm-object.c b/libnm/nm-object.c
index fbdbc1f6..143931f1 100644
--- a/libnm/nm-object.c
+++ b/libnm/nm-object.c
@@ -22,18 +22,18 @@
 #include "config.h"
 
 #include <string.h>
-#include <gio/gio.h>
-#include <glib/gi18n-lib.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <nm-utils.h>
+#include "nm-default.h"
 #include "nm-dbus-interface.h"
 #include "nm-object.h"
 #include "nm-object-cache.h"
 #include "nm-object-private.h"
-#include "nm-glib-compat.h"
 #include "nm-dbus-helpers.h"
 #include "nm-client.h"
+#include "nm-core-internal.h"
+#include "nm-macros-internal.h"
 
 static gboolean debug = FALSE;
 #define dbgmsg(f,...) if (G_UNLIKELY (debug)) { g_message (f, ## __VA_ARGS__ ); }
@@ -85,6 +85,10 @@ typedef struct {
 	NMObject *parent;
 	gboolean suppress_property_updates;
 
+	gboolean inited;        /* async init finished? */
+	GSList *waiters;        /* if async init did not finish, users of this object need
+	                         * to defer their notifications by adding themselves here. */
+
 	GSList *notify_items;
 	guint32 notify_id;
 
@@ -201,6 +205,24 @@ deferred_notify_cb (gpointer data)
 	if (priv->reload_remaining)
 		return G_SOURCE_REMOVE;
 
+	/* If not all added object are finished yet, then defer the deferred
+	 * notification. */
+	for (iter = priv->notify_items; iter; iter = g_slist_next (iter)) {
+		NotifyItem *item = iter->data;
+		NMObjectPrivate *item_priv;
+
+		if (!item->changed)
+			continue;
+
+		item_priv = NM_OBJECT_GET_PRIVATE (item->changed);
+		if (!item_priv->inited) {
+			if (!g_slist_find (item_priv->waiters, object))
+				item_priv->waiters = g_slist_prepend (item_priv->waiters,
+				                                      g_object_ref (object));
+			return G_SOURCE_REMOVE;
+		}
+	}
+
 	/* Clear priv->notify_items early so that an NMObject subclass that
 	 * listens to property changes can queue up other property changes
 	 * during the g_object_notify() call separately from the property
@@ -426,6 +448,7 @@ _nm_object_create (GType type, GDBusConnection *connection, const char *path)
 	 * before any external code sees it.
 	 */
 	_nm_object_cache_add (NM_OBJECT (object));
+	NM_OBJECT_GET_PRIVATE (object)->inited = TRUE;
 	if (!g_initable_init (G_INITABLE (object), NULL, &error)) {
 		dbgmsg ("Could not create object for %s: %s", path, error->message);
 		g_error_free (error);
@@ -460,6 +483,7 @@ create_async_inited (GObject *object, GAsyncResult *result, gpointer user_data)
 	NMObjectTypeAsyncData *async_data = user_data;
 	GError *error = NULL;
 
+	NM_OBJECT_GET_PRIVATE (object)->inited = TRUE;
 	if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error)) {
 		dbgmsg ("Could not create object for %s: %s",
 		        nm_object_get_path (NM_OBJECT (object)),
@@ -469,6 +493,19 @@ create_async_inited (GObject *object, GAsyncResult *result, gpointer user_data)
 	}
 
 	create_async_complete (object, async_data);
+
+	if (object) {
+		NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+		/* Re-queue notification checks for whoever was waiting for
+		 * this object to initialize. */
+		while (priv->waiters) {
+			NMObject *item = priv->waiters->data;
+			priv->waiters = g_slist_remove (priv->waiters, item);
+			_nm_object_defer_notify (item);
+			g_object_unref (item);
+		}
+	}
 }
 
 static void
@@ -511,7 +548,8 @@ create_async_got_property (GObject *proxy, GAsyncResult *result, gpointer user_d
 	GError *error = NULL;
 	GType type;
 
-	ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, &error);
+	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
+	                                  G_VARIANT_TYPE ("(v)"), &error);
 	if (ret) {
 		g_variant_get (ret, "(v)", &value);
 		type = type_data->type_func (value);
@@ -519,8 +557,8 @@ create_async_got_property (GObject *proxy, GAsyncResult *result, gpointer user_d
 		g_variant_unref (ret);
 	} else {
 		dbgmsg ("Could not fetch property '%s' of interface '%s' on %s: %s\n",
-		           type_data->property, type_data->interface, async_data->path,
-		           error->message);
+		        type_data->property, type_data->interface, async_data->path,
+		        error->message);
 		g_clear_error (&error);
 		type = G_TYPE_INVALID;
 	}
@@ -681,6 +719,8 @@ object_property_complete (ObjectCreatedData *odata)
 		for (i = 0; i < odata->length; i++)
 			add_to_object_array_unique (new, odata->objects[i]);
 
+		*((GPtrArray **) pi->field) = new;
+
 		if (pi->signal_prefix) {
 			GPtrArray *added = g_ptr_array_sized_new (3);
 			GPtrArray *removed = g_ptr_array_sized_new (3);
@@ -691,8 +731,6 @@ object_property_complete (ObjectCreatedData *odata)
 			/* Find objects in 'new' that do not exist in old */
 			array_diff (new, old, added);
 
-			*((GPtrArray **) pi->field) = new;
-
 			/* Emit added & removed */
 			for (i = 0; i < removed->len; i++) {
 				queue_added_removed_signal (self,
@@ -715,7 +753,6 @@ object_property_complete (ObjectCreatedData *odata)
 			/* No added/removed signals to send, just replace the property with
 			 * the new values.
 			 */
-			*((GPtrArray **) pi->field) = new;
 			different = TRUE;
 		}
 
@@ -962,27 +999,20 @@ process_properties_changed (NMObject *self, GVariant *properties, gboolean synch
 }
 
 static void
-property_proxy_signal (GDBusProxy *proxy,
-                       const char *sender_name,
-                       const char *signal_name,
-                       GVariant   *parameters,
-                       gpointer    user_data)
+properties_changed (GDBusProxy *proxy,
+                    GVariant   *properties,
+                    gpointer    user_data)
 {
-	GVariant *properties;
-
-	if (strcmp (signal_name, "PropertiesChanged") != 0)
-		return;
-
-	g_variant_get (parameters, "(@a{sv})", &properties);
 	process_properties_changed (NM_OBJECT (user_data), properties, FALSE);
-	g_variant_unref (properties);
 }
 
 #define HANDLE_TYPE(vtype, ctype, getter) \
 	G_STMT_START { \
 		if (g_variant_is_of_type (value, vtype)) { \
 			ctype *param = (ctype *) field; \
-			*param = getter (value); \
+			ctype newval = getter (value); \
+			different = *param != newval; \
+			*param = newval; \
 		} else { \
 			success = FALSE; \
 			goto done; \
@@ -996,20 +1026,29 @@ demarshal_generic (NMObject *object,
                    gpointer field)
 {
 	gboolean success = TRUE;
+	gboolean different = FALSE;
 
 	if (pspec->value_type == G_TYPE_STRING) {
 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) {
 			char **param = (char **) field;
-			g_free (*param);
-			*param = g_variant_dup_string (value, NULL);
+			const char *newval = g_variant_get_string (value, NULL);
+
+			different = !!g_strcmp0 (*param, newval);
+			if (different) {
+				g_free (*param);
+				*param = g_strdup (newval);
+			}
 		} else if (g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH)) {
 			char **param = (char **) field;
-			g_free (*param);
-			*param = g_variant_dup_string (value, NULL);
+			const char *newval = g_variant_get_string (value, NULL);
+
 			/* Handle "NULL" object paths */
-			if (g_strcmp0 (*param, "/") == 0) {
+			if (g_strcmp0 (newval, "/") == 0)
+				newval = NULL;
+			different = !!g_strcmp0 (*param, newval);
+			if (different) {
 				g_free (*param);
-				*param = NULL;
+				*param = g_strdup (newval);
 			}
 		} else {
 			success = FALSE;
@@ -1017,50 +1056,78 @@ demarshal_generic (NMObject *object,
 		}
 	} else if (pspec->value_type == G_TYPE_STRV) {
 		char ***param = (char ***)field;
-		if (*param)
-			g_strfreev (*param);
-		*param = g_variant_dup_strv (value, NULL);
+		const char **newval;
+		gsize i;
+
+		newval = g_variant_get_strv (value, NULL);
+		if (!*param)
+			different = TRUE;
+		else {
+			if (!_nm_utils_strv_equal ((char **) newval, *param)) {
+				different = TRUE;
+				g_strfreev (*param);
+			}
+		}
+		if (different) {
+			for (i = 0; newval[i]; i++)
+				newval[i] = g_strdup (newval[i]);
+			*param = (char **) newval;
+		} else
+			g_free (newval);
 	} else if (pspec->value_type == G_TYPE_BYTES) {
 		GBytes **param = (GBytes **)field;
-		gconstpointer val;
-		gsize length;
+		gconstpointer val, old_val = NULL;
+		gsize length, old_length = 0;
 
-		if (*param)
-			g_bytes_unref (*param);
 		val = g_variant_get_fixed_array (value, &length, 1);
-		if (length)
-			*param = g_bytes_new (val, length);
-		else
-			*param = NULL;
+
+		if (*param)
+			old_val = g_bytes_get_data (*param, &old_length);
+		different =    old_length != length
+		            || (   length > 0
+		                && memcmp (old_val, val, length) != 0);
+		if (different) {
+			if (*param)
+				g_bytes_unref (*param);
+			*param = length > 0 ? g_bytes_new (val, length) : NULL;
+		}
 	} else if (G_IS_PARAM_SPEC_ENUM (pspec)) {
 		int *param = (int *) field;
+		int newval = 0;
 
 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32))
-			*param = g_variant_get_int32 (value);
+			newval = g_variant_get_int32 (value);
 		else if (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32))
-			*param = g_variant_get_uint32 (value);
+			newval = g_variant_get_uint32 (value);
 		else {
 			success = FALSE;
 			goto done;
 		}
+		different = *param != newval;
+		*param = newval;
 	} else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
 		guint *param = (guint *) field;
+		guint newval = 0;
 
 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32))
-			*param = g_variant_get_int32 (value);
+			newval = g_variant_get_int32 (value);
 		else if (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32))
-			*param = g_variant_get_uint32 (value);
+			newval = g_variant_get_uint32 (value);
 		else {
 			success = FALSE;
 			goto done;
 		}
+		different = *param != newval;
+		*param = newval;
 	} else if (pspec->value_type == G_TYPE_BOOLEAN)
 		HANDLE_TYPE (G_VARIANT_TYPE_BOOLEAN, gboolean, g_variant_get_boolean);
 	else if (pspec->value_type == G_TYPE_UCHAR)
 		HANDLE_TYPE (G_VARIANT_TYPE_BYTE, guchar, g_variant_get_byte);
-	else if (pspec->value_type == G_TYPE_DOUBLE)
+	else if (pspec->value_type == G_TYPE_DOUBLE) {
+		NM_PRAGMA_WARNING_DISABLE("-Wfloat-equal")
 		HANDLE_TYPE (G_VARIANT_TYPE_DOUBLE, gdouble, g_variant_get_double);
-	else if (pspec->value_type == G_TYPE_INT)
+		NM_PRAGMA_WARNING_REENABLE
+	} else if (pspec->value_type == G_TYPE_INT)
 		HANDLE_TYPE (G_VARIANT_TYPE_INT32, gint, g_variant_get_int32);
 	else if (pspec->value_type == G_TYPE_UINT)
 		HANDLE_TYPE (G_VARIANT_TYPE_UINT32, guint, g_variant_get_uint32);
@@ -1083,7 +1150,8 @@ demarshal_generic (NMObject *object,
 
 done:
 	if (success) {
-		_nm_object_queue_notify (object, pspec->name);
+		if (different)
+			_nm_object_queue_notify (object, pspec->name);
 	} else {
 		dbgmsg ("%s: %s:%s (type %s) couldn't be set from D-Bus type %s.",
 		        __func__, G_OBJECT_TYPE_NAME (object), pspec->name,
@@ -1118,8 +1186,8 @@ _nm_object_register_properties (NMObject *object,
 	proxy = _nm_object_get_proxy (object, interface);
 	g_return_if_fail (proxy != NULL);
 
-	g_signal_connect (proxy, "g-signal",
-	                  G_CALLBACK (property_proxy_signal), object);
+	_nm_dbus_signal_connect (proxy, "PropertiesChanged", G_VARIANT_TYPE ("(a{sv})"),
+	                         G_CALLBACK (properties_changed), object);
 
 	instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 	priv->property_tables = g_slist_prepend (priv->property_tables, instance);
@@ -1157,11 +1225,12 @@ _nm_object_reload_properties (NMObject *object, GError **error)
 
 	g_hash_table_iter_init (&iter, priv->proxies);
 	while (g_hash_table_iter_next (&iter, (gpointer *) &interface, (gpointer *) &proxy)) {
-		ret = g_dbus_proxy_call_sync (priv->properties_proxy,
-		                              "GetAll",
-		                              g_variant_new ("(s)", interface),
-		                              G_DBUS_CALL_FLAGS_NONE, -1,
-		                              NULL, error);
+		ret = _nm_dbus_proxy_call_sync (priv->properties_proxy,
+		                                "GetAll",
+		                                g_variant_new ("(s)", interface),
+		                                G_VARIANT_TYPE ("(a{sv})"),
+		                                G_DBUS_CALL_FLAGS_NONE, -1,
+		                                NULL, error);
 		if (!ret) {
 			if (error && *error)
 				g_dbus_error_strip_remote_error (*error);
@@ -1204,11 +1273,12 @@ _nm_object_reload_property (NMObject *object,
 	if (!NM_OBJECT_GET_PRIVATE (object)->nm_running)
 		return;
 
-	ret = g_dbus_proxy_call_sync (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
-	                              "Get",
-	                              g_variant_new ("(ss)", interface, prop_name),
-	                              G_DBUS_CALL_FLAGS_NONE, 15000,
-	                              NULL, &err);
+	ret = _nm_dbus_proxy_call_sync (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
+	                                "Get",
+	                                g_variant_new ("(ss)", interface, prop_name),
+	                                G_VARIANT_TYPE ("(v)"),
+	                                G_DBUS_CALL_FLAGS_NONE, 15000,
+	                                NULL, &err);
 	if (!ret) {
 		dbgmsg ("%s: Error getting '%s' for %s: (%d) %s\n",
 		        __func__,
@@ -1268,10 +1338,7 @@ reload_complete (NMObject *object, gboolean emit_now)
 	GError *error;
 
 	if (emit_now) {
-		if (priv->notify_id) {
-			g_source_remove (priv->notify_id);
-			priv->notify_id = 0;
-		}
+		nm_clear_g_source (&priv->notify_id);
 		deferred_notify_cb (object);
 	} else
 		_nm_object_defer_notify (object);
@@ -1306,7 +1373,9 @@ reload_got_properties (GObject *proxy,
 	GVariant *ret, *props;
 	GError *error = NULL;
 
-	ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, &error);
+	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
+	                                  G_VARIANT_TYPE ("(a{sv})"),
+	                                  &error);
 	if (ret) {
 		g_variant_get (ret, "(@a{sv})", &props);
 		process_properties_changed (object, props, FALSE);
@@ -1679,14 +1748,12 @@ dispose (GObject *object)
 {
 	NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
 
-	if (priv->notify_id) {
-		g_source_remove (priv->notify_id);
-		priv->notify_id = 0;
-	}
+	nm_clear_g_source (&priv->notify_id);
 
 	g_slist_free_full (priv->notify_items, (GDestroyNotify) notify_item_free);
 	priv->notify_items = NULL;
 
+	g_slist_free_full (priv->waiters, g_object_unref);
 	g_clear_pointer (&priv->proxies, g_hash_table_unref);
 	g_clear_object (&priv->properties_proxy);