diff options
Diffstat (limited to 'libnm/nm-object.c')
| -rw-r--r-- | libnm/nm-object.c | 201 |
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); |