diff options
Diffstat (limited to 'libnm-glib/nm-object.c')
| -rw-r--r-- | libnm-glib/nm-object.c | 1096 |
1 files changed, 880 insertions, 216 deletions
diff --git a/libnm-glib/nm-object.c b/libnm-glib/nm-object.c index 9a8a60f7..fc9d7f86 100644 --- a/libnm-glib/nm-object.c +++ b/libnm-glib/nm-object.c @@ -22,6 +22,7 @@ */ #include <string.h> +#include <gio/gio.h> #include <nm-utils.h> #include "NetworkManager.h" #include "nm-object.h" @@ -29,28 +30,59 @@ #include "nm-object-private.h" #include "nm-dbus-glib-types.h" #include "nm-glib-compat.h" +#include "nm-types.h" #define DEBUG 0 -G_DEFINE_ABSTRACT_TYPE (NMObject, nm_object, G_TYPE_OBJECT) +static void nm_object_initable_iface_init (GInitableIface *iface); +static void nm_object_async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMObject, nm_object, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_object_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_object_async_initable_iface_init); + ) #define NM_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OBJECT, NMObjectPrivate)) +static GHashTable *type_funcs, *type_async_funcs; + typedef struct { - PropChangedMarshalFunc func; + PropertyMarshalFunc func; + GType object_type; + gpointer field; -} PropChangedInfo; +} PropertyInfo; + +static void reload_complete (NMObject *object); + +typedef struct { + PropertyInfo pi; + + NMObject *self; + DBusGProxy *proxy; + + char *get_method; + NMPseudoPropertyChangedFunc added_func; + NMPseudoPropertyChangedFunc removed_func; +} PseudoPropertyInfo; typedef struct { DBusGConnection *connection; char *path; DBusGProxy *properties_proxy; - GSList *pcs; + GSList *property_interfaces; + GSList *property_tables; + GHashTable *pseudo_properties; NMObject *parent; + gboolean suppress_property_updates; GSList *notify_props; guint32 notify_id; - gboolean disposed; + gboolean inited, disposed; + + GSList *reload_results; + guint reload_remaining; + GError *reload_error; } NMObjectPrivate; enum { @@ -77,8 +109,6 @@ constructor (GType type, object = G_OBJECT_CLASS (nm_object_parent_class)->constructor (type, n_construct_params, construct_params); - if (!object) - return NULL; priv = NM_OBJECT_GET_PRIVATE (object); @@ -88,17 +118,78 @@ constructor (GType type, return NULL; } - priv->properties_proxy = dbus_g_proxy_new_for_name (priv->connection, - NM_DBUS_SERVICE, - priv->path, - "org.freedesktop.DBus.Properties"); - _nm_object_cache_add (NM_OBJECT (object)); return object; } static void +constructed (GObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + if (G_OBJECT_CLASS (nm_object_parent_class)->constructed) + G_OBJECT_CLASS (nm_object_parent_class)->constructed (object); + + priv->properties_proxy = dbus_g_proxy_new_for_name (priv->connection, + NM_DBUS_SERVICE, + priv->path, + "org.freedesktop.DBus.Properties"); +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable); + + priv->inited = TRUE; + return _nm_object_reload_properties (NM_OBJECT (initable), error); +} + +static void +init_async_got_properties (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + priv->inited = TRUE; + if (_nm_object_reload_properties_finish (NM_OBJECT (object), result, &error)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, init_async); + _nm_object_reload_properties_async (NM_OBJECT (initable), init_async_got_properties, simple); +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + /* This is needed for now because of bug 667375; it can go away + * when we depend on glib >= 2.38 + */ + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return g_simple_async_result_get_op_res_gboolean (simple); +} + +static void dispose (GObject *object) { NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); @@ -118,6 +209,9 @@ dispose (GObject *object) g_slist_foreach (priv->notify_props, (GFunc) g_free, NULL); g_slist_free (priv->notify_props); + g_slist_foreach (priv->property_interfaces, (GFunc) g_free, NULL); + g_slist_free (priv->property_interfaces); + g_object_unref (priv->properties_proxy); dbus_g_connection_unref (priv->connection); @@ -129,10 +223,13 @@ finalize (GObject *object) { NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); - g_slist_foreach (priv->pcs, (GFunc) g_hash_table_destroy, NULL); - g_slist_free (priv->pcs); + g_slist_foreach (priv->property_tables, (GFunc) g_hash_table_destroy, NULL); + g_slist_free (priv->property_tables); g_free (priv->path); + if (priv->pseudo_properties) + g_hash_table_destroy (priv->pseudo_properties); + G_OBJECT_CLASS (nm_object_parent_class)->finalize (object); } @@ -187,8 +284,14 @@ nm_object_class_init (NMObjectClass *nm_object_class) g_type_class_add_private (nm_object_class, sizeof (NMObjectPrivate)); + if (!type_funcs) { + type_funcs = g_hash_table_new (NULL, NULL); + type_async_funcs = g_hash_table_new (NULL, NULL); + } + /* virtual methods */ object_class->constructor = constructor; + object_class->constructed = constructed; object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; @@ -223,6 +326,19 @@ nm_object_class_init (NMObjectClass *nm_object_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } +static void +nm_object_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_object_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} + /** * nm_object_get_connection: * @object: a #NMObject @@ -306,6 +422,142 @@ _nm_object_queue_notify (NMObject *object, const char *property) priv->notify_props = g_slist_prepend (priv->notify_props, g_strdup (property)); } +void +_nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func, + NMObjectTypeAsyncFunc type_async_func) +{ + g_hash_table_insert (type_funcs, + GSIZE_TO_POINTER (base_type), + type_func); + g_hash_table_insert (type_async_funcs, + GSIZE_TO_POINTER (base_type), + type_async_func); +} + +static GObject * +_nm_object_create (GType type, DBusGConnection *connection, const char *path) +{ + NMObjectTypeFunc type_func; + GObject *object; + GError *error = NULL; + + type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type)); + if (type_func) + type = type_func (connection, path); + + if (type == G_TYPE_INVALID) { + g_warning ("Could not create object for %s: unknown object type", path); + return NULL; + } + + object = g_object_new (type, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + if (!g_initable_init (G_INITABLE (object), NULL, &error)) { + g_object_unref (object); + object = NULL; + g_warning ("Could not create object for %s: %s", path, error->message); + g_error_free (error); + } + + return object; +} + +typedef void (*NMObjectCreateCallbackFunc) (GObject *, gpointer); +typedef struct { + DBusGConnection *connection; + char *path; + NMObjectCreateCallbackFunc callback; + gpointer user_data; +} NMObjectTypeAsyncData; + +static void +create_async_complete (GObject *object, NMObjectTypeAsyncData *async_data) +{ + async_data->callback (object, async_data->user_data); + + g_free (async_data->path); + g_slice_free (NMObjectTypeAsyncData, async_data); +} + +static void +async_inited (GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMObjectTypeAsyncData *async_data = user_data; + GObject *object = G_OBJECT (source); + GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error)) { + g_warning ("Could not create object for %s: %s", + nm_object_get_path (NM_OBJECT (object)), error->message); + g_error_free (error); + g_object_unref (object); + object = NULL; + } + + create_async_complete (object, async_data); +} + +static void +async_got_type (GType type, gpointer user_data) +{ + NMObjectTypeAsyncData *async_data = user_data; + GObject *object; + + /* Ensure we don't have the object already; we may get multiple type + * requests for the same object if there are multiple properties on + * other objects that refer to the object at this path. One of those + * other requests may have already completed. + */ + object = (GObject *) _nm_object_cache_get (async_data->path); + if (object) { + create_async_complete (object, async_data); + return; + } + + if (type == G_TYPE_INVALID) { + /* Don't know how to create this object */ + create_async_complete (NULL, async_data); + return; + } + + object = g_object_new (type, + NM_OBJECT_DBUS_CONNECTION, async_data->connection, + NM_OBJECT_DBUS_PATH, async_data->path, + NULL); + g_warn_if_fail (object != NULL); + g_async_initable_init_async (G_ASYNC_INITABLE (object), G_PRIORITY_DEFAULT, + NULL, async_inited, async_data); +} + +static void +_nm_object_create_async (GType type, DBusGConnection *connection, const char *path, + NMObjectCreateCallbackFunc callback, gpointer user_data) +{ + NMObjectTypeAsyncFunc type_async_func; + NMObjectTypeFunc type_func; + NMObjectTypeAsyncData *async_data; + + async_data = g_slice_new (NMObjectTypeAsyncData); + async_data->connection = connection; + async_data->path = g_strdup (path); + async_data->callback = callback; + async_data->user_data = user_data; + + type_async_func = g_hash_table_lookup (type_async_funcs, GSIZE_TO_POINTER (type)); + if (type_async_func) { + type_async_func (connection, path, async_got_type, async_data); + return; + } + + type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type)); + if (type_func) + type = type_func (connection, path); + + async_got_type (type, async_data); +} + /* Stolen from dbus-glib */ static char* wincaps_to_dash (const char *caps) @@ -328,19 +580,213 @@ wincaps_to_dash (const char *caps) return g_string_free (str, FALSE); } +/* Adds object to array if it's not already there */ +static void +add_to_object_array_unique (GPtrArray *array, GObject *obj) +{ + guint i; + + g_return_if_fail (array != NULL); + + if (obj != NULL) { + for (i = 0; i < array->len; i++) { + if (g_ptr_array_index (array, i) == obj) + return; + } + g_ptr_array_add (array, obj); + } +} + +typedef struct { + NMObject *self; + PropertyInfo *pi; + + GObject **objects; + int length, remaining; + + gboolean array; + const char *property_name; +} ObjectCreatedData; + static void -handle_property_changed (gpointer key, gpointer data, gpointer user_data) +object_property_complete (ObjectCreatedData *odata) +{ + NMObject *self = odata->self; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + PropertyInfo *pi = odata->pi; + + if (odata->array) { + GPtrArray **array = pi->field; + int i; + + if (*array) + g_boxed_free (NM_TYPE_OBJECT_ARRAY, *array); + *array = g_ptr_array_sized_new (odata->length); + for (i = 0; i < odata->length; i++) + add_to_object_array_unique (*array, odata->objects[i]); + } else { + GObject **obj_p = pi->field; + + if (*obj_p) + g_object_unref (*obj_p); + *obj_p = odata->objects[0]; + } + + if (odata->property_name) + _nm_object_queue_notify (self, odata->property_name); + + if (priv->reload_results && --priv->reload_remaining == 0) + reload_complete (self); + + g_object_unref (self); + g_free (odata->objects); + g_slice_free (ObjectCreatedData, odata); +} + +static void +object_created (GObject *obj, gpointer user_data) +{ + ObjectCreatedData *odata = user_data; + + /* We assume that on error, the creator_func printed something */ + + odata->objects[--odata->remaining] = obj; + if (!odata->remaining) + object_property_complete (odata); +} + +static gboolean +handle_object_property (NMObject *self, const char *property_name, GValue *value, + PropertyInfo *pi, gboolean synchronously) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + GObject *obj; + const char *path; + ObjectCreatedData *odata; + + odata = g_slice_new (ObjectCreatedData); + odata->self = g_object_ref (self); + odata->pi = pi; + odata->objects = g_new (GObject *, 1); + odata->length = odata->remaining = 1; + odata->array = FALSE; + odata->property_name = property_name; + + if (priv->reload_results) + priv->reload_remaining++; + + path = g_value_get_boxed (value); + if (!strcmp (path, "/")) { + object_created (NULL, odata); + return TRUE; + } + + obj = G_OBJECT (_nm_object_cache_get (path)); + if (obj) { + object_created (obj, odata); + return TRUE; + } else if (synchronously) { + obj = _nm_object_create (pi->object_type, priv->connection, path); + object_created (obj, odata); + return obj != NULL; + } else { + _nm_object_create_async (pi->object_type, priv->connection, path, + object_created, odata); + /* Assume success */ + return TRUE; + } +} + +static gboolean +handle_object_array_property (NMObject *self, const char *property_name, GValue *value, + PropertyInfo *pi, gboolean synchronously) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + GObject *obj; + GPtrArray *paths; + GPtrArray **array = pi->field; + const char *path; + ObjectCreatedData *odata; + int i; + + paths = g_value_get_boxed (value); + + odata = g_slice_new (ObjectCreatedData); + odata->self = g_object_ref (self); + odata->pi = pi; + odata->objects = g_new0 (GObject *, paths->len); + odata->length = odata->remaining = paths->len; + odata->array = TRUE; + odata->property_name = property_name; + + if (priv->reload_results) + priv->reload_remaining++; + + if (paths->len == 0) { + object_property_complete (odata); + return TRUE; + } + + for (i = 0; i < paths->len; i++) { + path = paths->pdata[i]; + if (!strcmp (path, "/")) { + /* FIXME: can't happen? */ + continue; + } + + obj = G_OBJECT (_nm_object_cache_get (path)); + if (obj) { + object_created (obj, odata); + } else if (synchronously) { + obj = _nm_object_create (pi->object_type, priv->connection, path); + object_created (obj, odata); + } else { + _nm_object_create_async (pi->object_type, priv->connection, path, + object_created, odata); + } + } + + if (!synchronously) { + /* Assume success */ + return TRUE; + } + + return *array && ((*array)->len == paths->len); +} + +static void +handle_property_changed (NMObject *self, const char *dbus_name, GValue *value, gboolean synchronously) { - NMObject *self = NM_OBJECT (user_data); NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); char *prop_name; - PropChangedInfo *pci; + PropertyInfo *pi; GParamSpec *pspec; gboolean success = FALSE, found = FALSE; GSList *iter; - GValue *value = data; - prop_name = wincaps_to_dash ((char *) key); + prop_name = wincaps_to_dash (dbus_name); + + /* Iterate through the object and its parents to find the property */ + for (iter = priv->property_tables; iter; iter = g_slist_next (iter)) { + pi = g_hash_table_lookup ((GHashTable *) iter->data, prop_name); + if (pi) { + if (!pi->field) { + /* We know about this property but aren't tracking changes on it. */ + goto out; + } + + found = TRUE; + break; + } + } + + if (!found) { +#if DEBUG + g_warning ("Property '%s' unhandled.", prop_name); +#endif + goto out; + } + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (self)), prop_name); if (!pspec) { g_warning ("%s: property '%s' changed but wasn't defined by object type %s.", @@ -350,29 +796,33 @@ handle_property_changed (gpointer key, gpointer data, gpointer user_data) goto out; } - /* Iterate through the object and its parents to find the property */ - for (iter = priv->pcs; iter; iter = g_slist_next (iter)) { - pci = g_hash_table_lookup ((GHashTable *) iter->data, prop_name); - if (pci) { - found = TRUE; - - /* Handle NULL object paths */ - if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) { - if (g_strcmp0 (g_value_get_boxed (value), "/") == 0) - value = NULL; - } - - success = (*(pci->func)) (self, pspec, value, pci->field); - if (success) - break; - } +#if DEBUG + { + char *s; + s = g_strdup_value_contents (value); + g_message ("PC: %p (%s) prop (%s) '%s' value (%s) %s", + self, G_OBJECT_TYPE_NAME (self), + g_type_name (pspec->value_type), prop_name, + G_VALUE_TYPE_NAME (value), s); + g_free (s); } - - if (!found) { +#endif + if (pi->object_type) { #if DEBUG - g_warning ("Property '%s' unhandled.", prop_name); + g_message (" Value is object type %s", g_type_name (pi->object_type)); #endif - } else if (!success) { + if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) + success = handle_object_property (self, pspec->name, value, pi, synchronously); + else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) + success = handle_object_array_property (self, pspec->name, value, pi, synchronously); + else { + g_warn_if_reached (); + goto out; + } + } else + success = (*(pi->func)) (self, pspec, value, pi->field); + + if (!success) { g_warning ("%s: failed to update property '%s' of object type %s.", __func__, prop_name, @@ -383,10 +833,19 @@ out: g_free (prop_name); } -void -_nm_object_process_properties_changed (NMObject *self, GHashTable *properties) +static void +process_properties_changed (NMObject *self, GHashTable *properties, gboolean synchronously) { - g_hash_table_foreach (properties, handle_property_changed, self); + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + GHashTableIter iter; + gpointer name, value; + + if (priv->suppress_property_updates) + return; + + g_hash_table_iter_init (&iter, properties); + while (g_hash_table_iter_next (&iter, &name, &value)) + handle_property_changed (self, name, value, synchronously); } static void @@ -394,49 +853,7 @@ properties_changed_proxy (DBusGProxy *proxy, GHashTable *properties, gpointer user_data) { - _nm_object_process_properties_changed (NM_OBJECT (user_data), properties); -} - -void -_nm_object_handle_properties_changed (NMObject *object, - DBusGProxy *proxy, - const NMPropertiesChangedInfo *info) -{ - NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); - NMPropertiesChangedInfo *tmp; - GHashTable *instance; - - g_return_if_fail (NM_IS_OBJECT (object)); - g_return_if_fail (proxy != NULL); - g_return_if_fail (info != NULL); - - dbus_g_proxy_add_signal (proxy, "PropertiesChanged", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (proxy, - "PropertiesChanged", - G_CALLBACK (properties_changed_proxy), - object, - NULL); - - instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - priv->pcs = g_slist_prepend (priv->pcs, instance); - - for (tmp = (NMPropertiesChangedInfo *) info; tmp->name; tmp++) { - PropChangedInfo *pci; - - if (!tmp->name || !tmp->func || !tmp->field) { - g_warning ("%s: missing field in NMPropertiesChangedInfo", __func__); - continue; - } - - pci = g_malloc0 (sizeof (PropChangedInfo)); - if (!pci) { - g_warning ("%s: not enough memory for PropChangedInfo", __func__); - continue; - } - pci->func = tmp->func; - pci->field = tmp->field; - g_hash_table_insert (instance, g_strdup (tmp->name), pci); - } + process_properties_changed (NM_OBJECT (user_data), properties, FALSE); } #define HANDLE_TYPE(ucase, lcase, getter) \ @@ -449,11 +866,11 @@ _nm_object_handle_properties_changed (NMObject *object, goto done; \ } -gboolean -_nm_object_demarshal_generic (NMObject *object, - GParamSpec *pspec, - GValue *value, - gpointer field) +static gboolean +demarshal_generic (NMObject *object, + GParamSpec *pspec, + GValue *value, + gpointer field) { gboolean success = TRUE; @@ -503,34 +920,132 @@ done: return success; } +void +_nm_object_register_properties (NMObject *object, + DBusGProxy *proxy, + const NMPropertiesInfo *info) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + NMPropertiesInfo *tmp; + GHashTable *instance; + + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (proxy != NULL); + g_return_if_fail (info != NULL); + + priv->property_interfaces = g_slist_prepend (priv->property_interfaces, + g_strdup (dbus_g_proxy_get_interface (proxy))); + + dbus_g_proxy_add_signal (proxy, "PropertiesChanged", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (proxy, + "PropertiesChanged", + G_CALLBACK (properties_changed_proxy), + object, + NULL); + + 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); + + for (tmp = (NMPropertiesInfo *) info; tmp->name; tmp++) { + PropertyInfo *pi; + + if (!tmp->name || (tmp->func && !tmp->field)) { + g_warning ("%s: missing field in NMPropertiesInfo", __func__); + continue; + } + + pi = g_malloc0 (sizeof (PropertyInfo)); + pi->func = tmp->func ? tmp->func : demarshal_generic; + pi->object_type = tmp->object_type; + pi->field = tmp->field; + g_hash_table_insert (instance, g_strdup (tmp->name), pi); + } +} + gboolean -_nm_object_get_property (NMObject *object, - const char *interface, - const char *prop_name, - GValue *value, - GError **error) +_nm_object_reload_properties (NMObject *object, GError **error) { + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GHashTable *props = NULL; + GSList *p; + GHashTableIter pp; + gpointer name, info; + + if (!priv->property_interfaces) + return TRUE; + + for (p = priv->property_interfaces; p; p = p->next) { + if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error, + G_TYPE_STRING, p->data, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) + return FALSE; + + process_properties_changed (object, props, TRUE); + g_hash_table_destroy (props); + } + + if (priv->pseudo_properties) { + g_hash_table_iter_init (&pp, priv->pseudo_properties); + while (g_hash_table_iter_next (&pp, &name, &info)) + _nm_object_reload_pseudo_property (object, name); + } + + return TRUE; +} + +void +_nm_object_suppress_property_updates (NMObject *object, gboolean suppress) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + priv->suppress_property_updates = suppress; +} + + +void +_nm_object_ensure_inited (NMObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GError *error = NULL; + + if (!priv->inited) { + if (!g_initable_init (G_INITABLE (object), NULL, &error)) { + g_warning ("Could not initialize %s %s: %s", + G_OBJECT_TYPE_NAME (object), priv->path, + error->message); + g_error_free (error); + + /* Only warn once */ + priv->inited = TRUE; + } + } +} + +void +_nm_object_reload_property (NMObject *object, + const char *interface, + const char *prop_name) +{ + GValue value = { 0, }; GError *err = NULL; - g_return_val_if_fail (NM_IS_OBJECT (object), FALSE); - g_return_val_if_fail (interface != NULL, FALSE); - g_return_val_if_fail (prop_name != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (interface != NULL); + g_return_if_fail (prop_name != NULL); if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy, "Get", 15000, &err, G_TYPE_STRING, interface, G_TYPE_STRING, prop_name, G_TYPE_INVALID, - G_TYPE_VALUE, value, + G_TYPE_VALUE, &value, G_TYPE_INVALID)) { /* Don't warn about D-Bus no reply/timeout errors; it's mostly noise and * happens for example when NM quits and the applet is still running. - * And don't warn when 'error' is not NULL, rather propagate 'err' so the caller - * can do something with it. */ - if ( !error - && !(err->domain == DBUS_GERROR && err->code == DBUS_GERROR_NO_REPLY)) { + */ + if (!g_error_matches (err, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) { g_warning ("%s: Error getting '%s' for %s: (%d) %s\n", __func__, prop_name, @@ -538,11 +1053,12 @@ _nm_object_get_property (NMObject *object, err->code, err->message); } - g_propagate_error (error, err); - return FALSE; + g_clear_error (&err); + return; } - return TRUE; + handle_property_changed (object, prop_name, &value, TRUE); + g_value_unset (&value); } void @@ -569,157 +1085,305 @@ _nm_object_set_property (NMObject *object, } } -char * -_nm_object_get_string_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +static void +pseudo_property_object_created (GObject *obj, gpointer user_data) { - char *str = NULL; - const char *tmp; - GValue value = {0,}; + PseudoPropertyInfo *ppi = user_data; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - if (G_VALUE_HOLDS_STRING (&value)) - str = g_strdup (g_value_get_string (&value)); - else if (G_VALUE_HOLDS (&value, DBUS_TYPE_G_OBJECT_PATH)) { - tmp = g_value_get_boxed (&value); - /* Handle "NULL" object paths */ - if (g_strcmp0 (tmp, "/") != 0) - str = g_strdup (tmp); - } - g_value_unset (&value); - } + if (obj) { + GPtrArray **list_p = (GPtrArray **)ppi->pi.field; - return str; + if (!*list_p) + *list_p = g_ptr_array_new (); + add_to_object_array_unique (*list_p, obj); + ppi->added_func (ppi->self, NM_OBJECT (obj)); + } } -char * -_nm_object_get_object_path_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +static void +pseudo_property_added (DBusGProxy *proxy, const char *path, gpointer user_data) { - char *path = NULL; - const char *tmp; - GValue value = {0,}; + PseudoPropertyInfo *ppi = user_data; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (ppi->self); + NMObject *obj; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - tmp = g_value_get_boxed (&value); - if (g_strcmp0 (tmp, "/") != 0) - path = g_strdup (tmp); - g_value_unset (&value); - } + if (priv->suppress_property_updates) + return; - return path; + obj = _nm_object_cache_get (path); + if (obj) + pseudo_property_object_created (G_OBJECT (obj), ppi); + else { + _nm_object_create_async (ppi->pi.object_type, priv->connection, path, + pseudo_property_object_created, ppi); + } } -gint32 -_nm_object_get_int_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +static void +pseudo_property_removed (DBusGProxy *proxy, const char *path, gpointer user_data) { - gint32 i = 0; - GValue value = {0,}; + PseudoPropertyInfo *ppi = user_data; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (ppi->self); + GPtrArray *list = *(GPtrArray **)ppi->pi.field; + NMObject *obj = NULL; + int i; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - i = g_value_get_int (&value); - g_value_unset (&value); + if (!list || priv->suppress_property_updates) + return; + + for (i = 0; i < list->len; i++) { + obj = list->pdata[i]; + if (!strcmp (path, nm_object_get_path (obj))) { + g_ptr_array_remove_index (list, i); + ppi->removed_func (ppi->self, obj); + g_object_unref (obj); + return; + } } +} - return i; +static void +free_pseudo_property (PseudoPropertyInfo *ppi) +{ + g_object_unref (ppi->proxy); + g_free (ppi->get_method); + g_slice_free (PseudoPropertyInfo, ppi); } -guint32 -_nm_object_get_uint_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +void +_nm_object_register_pseudo_property (NMObject *object, + DBusGProxy *proxy, + const char *name, + gpointer field, + GType object_type, + NMPseudoPropertyChangedFunc added_func, + NMPseudoPropertyChangedFunc removed_func) { - guint32 i = 0; - GValue value = {0,}; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + PseudoPropertyInfo *ppi; + int basename_len; + char *added_signal, *removed_signal; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - i = g_value_get_uint (&value); - g_value_unset (&value); - } + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (proxy != NULL); - return i; + ppi = g_slice_new0 (PseudoPropertyInfo); + ppi->pi.field = field; + ppi->pi.object_type = object_type; + ppi->self = object; + ppi->proxy = g_object_ref (proxy); + ppi->added_func = added_func; + ppi->removed_func = removed_func; + + basename_len = strlen (name); + if (basename_len > 4 && !strcmp (name + basename_len - 4, "List")) + basename_len -= 4; + else if (basename_len > 1 && name[basename_len - 1] == 's') + basename_len--; + else + g_assert_not_reached (); + + ppi->get_method = g_strdup_printf ("Get%s", name); + added_signal = g_strdup_printf ("%.*sAdded", basename_len, name); + removed_signal = g_strdup_printf ("%.*sRemoved", basename_len, name); + + if (!priv->pseudo_properties) { + priv->pseudo_properties = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) free_pseudo_property); + } + g_hash_table_insert (priv->pseudo_properties, g_strdup (name), ppi); + + dbus_g_proxy_add_signal (proxy, added_signal, + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (proxy, added_signal, + G_CALLBACK (pseudo_property_added), + ppi, NULL); + + dbus_g_proxy_add_signal (proxy, removed_signal, + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (proxy, removed_signal, + G_CALLBACK (pseudo_property_removed), + ppi, NULL); + + g_free (added_signal); + g_free (removed_signal); } -gboolean -_nm_object_get_boolean_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +void +_nm_object_reload_pseudo_property (NMObject *object, + const char *name) { - gboolean b = FALSE; - GValue value = {0,}; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + PseudoPropertyInfo *ppi; + GPtrArray *temp; + GError *error = NULL; + GValue value = { 0, }; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - b = g_value_get_boolean (&value); - g_value_unset (&value); + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (name != NULL); + + ppi = g_hash_table_lookup (priv->pseudo_properties, name); + g_return_if_fail (ppi != NULL); + + if (!dbus_g_proxy_call (ppi->proxy, ppi->get_method, &error, + G_TYPE_INVALID, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &temp, + G_TYPE_INVALID)) { + g_warning ("%s: error calling %s: %s", __func__, ppi->get_method, error->message); + g_error_free (error); + return; } - return b; + g_value_init (&value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH); + g_value_take_boxed (&value, temp); + handle_object_array_property (object, NULL, &value, &ppi->pi, TRUE); + g_value_unset (&value); } -gint8 -_nm_object_get_byte_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +static void +reload_complete (NMObject *object) { - gint8 b = G_MAXINT8; - GValue value = {0,}; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GSimpleAsyncResult *simple; + GSList *results, *iter; + GError *error; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - b = g_value_get_uchar (&value); - g_value_unset (&value); - } + results = priv->reload_results; + priv->reload_results = NULL; + error = priv->reload_error; + priv->reload_error = NULL; + + for (iter = results; iter; iter = iter->next) { + simple = results->data; - return b; + if (error) + g_simple_async_result_set_from_error (simple, error); + else + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + + g_simple_async_result_complete (simple); + g_object_unref (simple); + } + g_slist_free (results); + g_clear_error (&error); } -gdouble -_nm_object_get_double_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +static void +reload_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) { - gdouble d = G_MAXDOUBLE; - GValue value = {0,}; + NMObject *object = user_data; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GHashTable *props = NULL; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + process_properties_changed (object, props, FALSE); + g_hash_table_destroy (props); + } else { + if (priv->reload_error) + g_error_free (error); + else + priv->reload_error = error; + } - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - d = g_value_get_double (&value); + if (--priv->reload_remaining == 0) + reload_complete (object); +} + +static void +reload_got_pseudo_property (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) +{ + PseudoPropertyInfo *ppi = user_data; + NMObject *object = ppi->self; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GPtrArray *temp; + GValue value = { 0, }; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &temp, + G_TYPE_INVALID)) { + g_value_init (&value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH); + g_value_take_boxed (&value, temp); + if (!priv->suppress_property_updates) + handle_object_array_property (object, NULL, &value, &ppi->pi, FALSE); g_value_unset (&value); + } else { + if (priv->reload_error) + g_error_free (error); + else + priv->reload_error = error; } - return d; + if (--priv->reload_remaining == 0) + reload_complete (object); } -GByteArray * -_nm_object_get_byte_array_property (NMObject *object, - const char *interface, - const char *prop_name, - GError **error) +void +_nm_object_reload_properties_async (NMObject *object, GAsyncReadyCallback callback, gpointer user_data) { - GByteArray *array = NULL; - GValue value = {0,}; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GSimpleAsyncResult *simple; + GSList *p; - if (_nm_object_get_property (object, interface, prop_name, &value, error)) { - GArray * tmp = g_value_get_boxed (&value); - int i; - unsigned char byte; + simple = g_simple_async_result_new (G_OBJECT (object), callback, + user_data, _nm_object_reload_properties_async); + + if (!priv->property_interfaces && !priv->pseudo_properties) { + g_simple_async_result_complete_in_idle (simple); + return; + } + + priv->reload_results = g_slist_prepend (priv->reload_results, simple); + + /* If there was already a reload happening, we don't need to + * re-read the properties again, we just need to wait for the + * existing reload to finish. + */ + if (priv->reload_results->next) + return; - array = g_byte_array_sized_new (tmp->len); - for (i = 0; i < tmp->len; i++) { - byte = g_array_index (tmp, unsigned char, i); - g_byte_array_append (array, &byte, 1); + for (p = priv->property_interfaces; p; p = p->next) { + priv->reload_remaining++; + dbus_g_proxy_begin_call (priv->properties_proxy, "GetAll", + reload_got_properties, object, NULL, + G_TYPE_STRING, p->data, + G_TYPE_INVALID); + } + + if (priv->pseudo_properties) { + GHashTableIter iter; + gpointer key, value; + PseudoPropertyInfo *ppi; + + g_hash_table_iter_init (&iter, priv->pseudo_properties); + while (g_hash_table_iter_next (&iter, &key, &value)) { + ppi = value; + priv->reload_remaining++; + dbus_g_proxy_begin_call (ppi->proxy, ppi->get_method, + reload_got_pseudo_property, ppi, NULL, + G_TYPE_INVALID); } - g_value_unset (&value); } +} + +gboolean +_nm_object_reload_properties_finish (NMObject *object, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (NM_IS_OBJECT (object), FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (object), _nm_object_reload_properties_async), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; - return array; + return g_simple_async_result_get_op_res_gboolean (simple); } |