1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright 2014-2015 Red Hat, Inc.
21 #include "nm-default.h"
26 #include "nm-exported-object.h"
27 #include "nm-bus-manager.h"
29 static GHashTable *prefix_counters;
30 static gboolean quitting = FALSE;
33 #if NM_MORE_ASSERTS >= 2
34 #define _ASSERT_NO_EARLY_EXPORT
37 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMExportedObject, nm_exported_object, G_TYPE_DBUS_OBJECT_SKELETON,
38 prefix_counters = g_hash_table_new (g_str_hash, g_str_equal);
44 NMBusManager *bus_mgr;
47 GHashTable *pending_notifies;
50 #ifdef _ASSERT_NO_EARLY_EXPORT
51 gboolean _constructed;
53 } NMExportedObjectPrivate;
55 #define NM_EXPORTED_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_EXPORTED_OBJECT, NMExportedObjectPrivate))
58 GHashTable *properties;
59 GSList *skeleton_types;
61 } NMExportedObjectClassInfo;
63 GQuark nm_exported_object_class_info_quark (void);
64 G_DEFINE_QUARK (NMExportedObjectClassInfo, nm_exported_object_class_info)
66 /*****************************************************************************/
68 #define _NMLOG_PREFIX_NAME "exported-object"
69 #define _NMLOG_DOMAIN LOGD_CORE
71 #define _NMLOG(level, ...) \
72 nm_log (level, _NMLOG_DOMAIN, \
73 "%s[%p]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
74 _NMLOG_PREFIX_NAME, (self) \
75 _NM_UTILS_MACRO_REST (__VA_ARGS__))
77 /*****************************************************************************/
79 /* "AddConnectionUnsaved" -> "handle-add-connection-unsaved" */
81 nm_exported_object_skeletonify_method_name (const char *dbus_method_name)
86 out = g_string_new ("handle");
87 for (p = dbus_method_name; *p; p++) {
88 if (g_ascii_isupper (*p) || p == dbus_method_name) {
89 g_string_append_c (out, '-');
90 g_string_append_c (out, g_ascii_tolower (*p));
92 g_string_append_c (out, *p);
95 return g_string_free (out, FALSE);
98 /* "can-modify" -> "CanModify" */
100 dbusify_name (const char *gobject_name)
104 gboolean capitalize = TRUE;
106 out = g_string_new ("");
107 for (p = gobject_name; *p; p++) {
109 g_string_append_c (out, g_ascii_toupper (*p));
111 } else if (*p == '-')
114 g_string_append_c (out, *p);
117 return g_string_free (out, FALSE);
120 /* "can_modify" -> "can-modify". Returns %NULL if @gobject_name contains no underscores */
122 hyphenify_name (const char *gobject_name)
124 char *hyphen_name, *p;
126 if (!strchr (gobject_name, '_'))
129 hyphen_name = g_strdup (gobject_name);
130 for (p = hyphen_name; *p; p++) {
137 /* Called when an #NMExportedObject emits a signal that corresponds to a D-Bus
138 * signal, and re-emits that signal on the correct skeleton object as well.
141 nm_exported_object_signal_hook (GSignalInvocationHint *ihint,
142 guint n_param_values,
143 const GValue *param_values,
146 NMExportedObject *self = g_value_get_object (¶m_values[0]);
147 NMExportedObjectPrivate *priv;
148 GSignalQuery *signal_info = data;
149 GDBusInterfaceSkeleton *interface = NULL;
151 GValue *dbus_param_values;
154 priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
158 for (iter = priv->interfaces; iter; iter = iter->next) {
159 if (g_type_is_a (G_OBJECT_TYPE (iter->data), signal_info->itype)) {
160 interface = G_DBUS_INTERFACE_SKELETON (iter->data);
164 g_return_val_if_fail (interface != NULL, TRUE);
166 dbus_param_values = g_new0 (GValue, n_param_values);
167 g_value_init (&dbus_param_values[0], G_OBJECT_TYPE (interface));
168 g_value_set_object (&dbus_param_values[0], interface);
169 for (i = 1; i < n_param_values; i++) {
170 if (g_type_is_a (param_values[i].g_type, NM_TYPE_EXPORTED_OBJECT)) {
171 NMExportedObject *arg = g_value_get_object (¶m_values[i]);
173 g_value_init (&dbus_param_values[i], G_TYPE_STRING);
174 if (arg && nm_exported_object_is_exported (arg))
175 g_value_set_string (&dbus_param_values[i], nm_exported_object_get_path (arg));
177 g_value_set_string (&dbus_param_values[i], "/");
179 g_value_init (&dbus_param_values[i], param_values[i].g_type);
180 g_value_copy (¶m_values[i], &dbus_param_values[i]);
184 g_signal_emitv (dbus_param_values, signal_info->signal_id, 0, NULL);
186 for (i = 0; i < n_param_values; i++)
187 g_value_unset (&dbus_param_values[i]);
188 g_free (dbus_param_values);
194 * nm_exported_object_class_add_interface:
195 * @object_class: an #NMExportedObjectClass
196 * @dbus_skeleton_type: the type of the #GDBusInterfaceSkeleton to add
197 * @...: method name / handler pairs, %NULL-terminated
199 * Adds @dbus_skeleton_type to the list of D-Bus interfaces implemented by
200 * @object_class. Instances of @object_class will automatically have a skeleton
201 * of that type created, which will be exported when you call
202 * nm_exported_object_export().
204 * The skeleton's properties will be initialized from the #NMExportedObject's,
205 * and bidirectional bindings will be set up between them. When exported
206 * properties change, both the org.freedesktop.DBus.Properties.PropertiesChanged
207 * signal and the traditional NetworkManager PropertiesChanged signal will be
210 * When a signal is emitted on an #NMExportedObject that has the same name as a
211 * signal on @dbus_skeleton_type, it will automatically be emitted on the
212 * skeleton as well; #NMExportedObject arguments in the signal will be converted
213 * to D-Bus object paths in the skeleton signal.
215 * The arguments after @dbus_skeleton_type are pairs of D-Bus method names (in
216 * CamelCase), and the corresponding handlers for them (which must have the same
217 * prototype as the corresponding "handle-..." signal on @dbus_skeleton_type,
218 * except with no return value, and with the first argument being an object of
219 * @object_class's type, not of @dbus_skeleton_type).
221 * It is a programmer error if:
222 * - @object_class does not define a property of the same name and type as
223 * each of @dbus_skeleton_type's properties.
224 * - @object_class does not define a signal with the same name and arguments
225 * as each of @dbus_skeleton_type's signals.
226 * - the list of method names includes any names that do not correspond to
227 * "handle-" signals on @dbus_skeleton_type.
228 * - the list of method names does not include every method defined by
229 * @dbus_skeleton_type.
232 nm_exported_object_class_add_interface (NMExportedObjectClass *object_class,
233 GType dbus_skeleton_type,
236 NMExportedObjectClassInfo *classinfo;
237 NMExportedObjectDBusMethodImpl method;
239 const char *method_name;
241 gs_free GType *interfaces = NULL;
243 guint n_signals, n_method_signals;
244 guint object_signal_id;
247 GObjectClass *dbus_object_class;
248 gs_free GParamSpec **dbus_properties = NULL;
249 GParamSpec *object_property;
250 guint n_dbus_properties;
252 g_return_if_fail (NM_IS_EXPORTED_OBJECT_CLASS (object_class));
253 g_return_if_fail (g_type_is_a (dbus_skeleton_type, G_TYPE_DBUS_INTERFACE_SKELETON));
255 classinfo = g_slice_new (NMExportedObjectClassInfo);
256 classinfo->skeleton_types = NULL;
257 classinfo->methods = g_array_new (FALSE, FALSE, sizeof (NMExportedObjectDBusMethodImpl));
258 classinfo->properties = g_hash_table_new (g_str_hash, g_str_equal);
259 g_type_set_qdata (G_TYPE_FROM_CLASS (object_class),
260 nm_exported_object_class_info_quark (), classinfo);
262 classinfo->skeleton_types = g_slist_prepend (classinfo->skeleton_types,
263 GSIZE_TO_POINTER (dbus_skeleton_type));
265 /* Ensure @dbus_skeleton_type's class_init has run, so its signals/properties
268 dbus_object_class = g_type_class_ref (dbus_skeleton_type);
270 /* Add method implementations from the varargs */
271 va_start (ap, dbus_skeleton_type);
272 while ((method_name = va_arg (ap, const char *)) && (impl = va_arg (ap, GCallback))) {
273 method.dbus_skeleton_type = dbus_skeleton_type;
274 method.method_name = nm_exported_object_skeletonify_method_name (method_name);
275 g_assert (g_signal_lookup (method.method_name, dbus_skeleton_type) != 0);
278 g_array_append_val (classinfo->methods, method);
283 dbus_properties = g_object_class_list_properties (dbus_object_class, &n_dbus_properties);
284 for (i = 0; i < n_dbus_properties; i++) {
287 if (g_str_has_prefix (dbus_properties[i]->name, "g-"))
290 object_property = g_object_class_find_property (G_OBJECT_CLASS (object_class),
291 dbus_properties[i]->name);
292 g_assert (object_property != NULL);
293 g_assert (object_property->value_type == dbus_properties[i]->value_type);
295 g_assert (!g_hash_table_contains (classinfo->properties, dbus_properties[i]->name));
296 g_hash_table_insert (classinfo->properties,
297 g_strdup (dbus_properties[i]->name),
298 dbusify_name (dbus_properties[i]->name));
299 hyphen_name = hyphenify_name (dbus_properties[i]->name);
301 g_assert (!g_hash_table_contains (classinfo->properties, hyphen_name));
302 g_hash_table_insert (classinfo->properties,
304 dbusify_name (dbus_properties[i]->name));
308 /* Signals. Unlike g_object_class_list_properties(), g_signal_list_ids() is
309 * "shallow", so we need to query each implemented gdbus-generated interface
312 interfaces = g_type_interfaces (dbus_skeleton_type, &n_interfaces);
313 n_method_signals = 0;
314 for (i = 0; i < n_interfaces; i++) {
315 gs_free guint *dbus_signals = NULL;
317 dbus_signals = g_signal_list_ids (interfaces[i], &n_signals);
318 for (s = 0; s < n_signals; s++) {
319 g_signal_query (dbus_signals[s], &query);
321 /* PropertiesChanged is handled specially */
322 if (!strcmp (query.signal_name, "properties-changed"))
325 if (g_str_has_prefix (query.signal_name, "handle-")) {
330 object_signal_id = g_signal_lookup (query.signal_name, G_TYPE_FROM_CLASS (object_class));
331 g_assert (object_signal_id != 0);
333 g_signal_add_emission_hook (object_signal_id, 0,
334 nm_exported_object_signal_hook,
335 g_memdup (&query, sizeof (query)),
340 g_assert_cmpint (n_method_signals, ==, classinfo->methods->len);
342 g_type_class_unref (dbus_object_class);
345 /* "meta-marshaller" that receives the skeleton "handle-foo" signal, replaces
346 * the skeleton object with an #NMExportedObject in the parameters, drops the
347 * user_data parameter, and adds a "TRUE" return value (indicating to gdbus that
348 * the signal was handled).
351 nm_exported_object_meta_marshal (GClosure *closure, GValue *return_value,
352 guint n_param_values, const GValue *param_values,
353 gpointer invocation_hint, gpointer marshal_data)
355 GValue *local_param_values;
357 local_param_values = g_new0 (GValue, n_param_values);
358 g_value_init (&local_param_values[0], G_TYPE_POINTER);
359 g_value_set_pointer (&local_param_values[0], closure->data);
360 memcpy (local_param_values + 1, param_values + 1, (n_param_values - 1) * sizeof (GValue));
362 g_cclosure_marshal_generic (closure, NULL,
363 n_param_values, local_param_values,
365 ((GCClosure *)closure)->callback);
366 g_value_set_boolean (return_value, TRUE);
368 g_value_unset (&local_param_values[0]);
369 g_free (local_param_values);
372 GQuark _skeleton_data_quark (void);
373 G_DEFINE_QUARK (skeleton-data, _skeleton_data);
376 GBinding **prop_bindings;
377 gulong *method_signals;
380 GDBusInterfaceSkeleton *
381 nm_exported_object_skeleton_create (GType dbus_skeleton_type,
382 GObjectClass *object_class,
383 const NMExportedObjectDBusMethodImpl *methods,
387 GDBusInterfaceSkeleton *interface;
388 gs_free GParamSpec **properties = NULL;
389 SkeletonData *skeleton_data;
393 interface = G_DBUS_INTERFACE_SKELETON (g_object_new (dbus_skeleton_type, NULL));
395 skeleton_data = g_slice_new (SkeletonData);
397 /* Bind properties */
398 properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (interface), &n_properties);
399 skeleton_data->prop_bindings = g_new (GBinding *, n_properties + 1);
400 for (i = 0, j = 0; i < n_properties; i++) {
401 GParamSpec *nm_property;
403 GBinding *prop_binding;
405 nm_property = g_object_class_find_property (object_class, properties[i]->name);
409 flags = G_BINDING_SYNC_CREATE;
410 if ( (nm_property->flags & G_PARAM_WRITABLE)
411 && !(nm_property->flags & G_PARAM_CONSTRUCT_ONLY))
412 flags |= G_BINDING_BIDIRECTIONAL;
413 prop_binding = g_object_bind_property (target, properties[i]->name,
414 interface, properties[i]->name,
417 skeleton_data->prop_bindings[j++] = prop_binding;
419 skeleton_data->prop_bindings[j++] = NULL;
422 skeleton_data->method_signals = g_new (gulong, methods_len + 1);
423 for (i = 0, j = 0; i < methods_len; i++) {
424 const NMExportedObjectDBusMethodImpl *method = &methods[i];
426 gulong method_signal;
428 /* ignore methods that are for a different skeleton-type. */
429 if ( method->dbus_skeleton_type
430 && method->dbus_skeleton_type != dbus_skeleton_type)
433 closure = g_cclosure_new_swap (method->impl, target, NULL);
434 g_closure_set_meta_marshal (closure, NULL, nm_exported_object_meta_marshal);
435 method_signal = g_signal_connect_closure (interface, method->method_name, closure, FALSE);
437 if (method_signal != 0)
438 skeleton_data->method_signals[j++] = method_signal;
440 skeleton_data->method_signals[j++] = 0;
442 g_object_set_qdata ((GObject *) interface, _skeleton_data_quark (), skeleton_data);
448 nm_exported_object_create_skeletons (NMExportedObject *self,
451 NMExportedObjectPrivate *priv;
452 GObjectClass *object_class;
453 NMExportedObjectClassInfo *classinfo;
455 GDBusInterfaceSkeleton *interface;
456 const NMExportedObjectDBusMethodImpl *methods;
459 classinfo = g_type_get_qdata (object_type, nm_exported_object_class_info_quark ());
463 object_class = g_type_class_peek (object_type);
464 priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
466 methods = classinfo->methods->len ? &g_array_index (classinfo->methods, NMExportedObjectDBusMethodImpl, 0) : NULL;
467 methods_len = classinfo->methods->len;
469 for (iter = classinfo->skeleton_types; iter; iter = iter->next) {
470 interface = nm_exported_object_skeleton_create (GPOINTER_TO_SIZE (iter->data),
476 g_dbus_object_skeleton_add_interface ((GDBusObjectSkeleton *) self, interface);
478 priv->interfaces = g_slist_prepend (priv->interfaces, interface);
483 nm_exported_object_skeleton_release (GDBusInterfaceSkeleton *interface)
485 SkeletonData *skeleton_data;
488 g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface));
490 skeleton_data = g_object_steal_qdata ((GObject *) interface, _skeleton_data_quark ());
492 for (j = 0; skeleton_data->prop_bindings[j]; j++)
493 g_object_unref (skeleton_data->prop_bindings[j]);
494 for (j = 0; skeleton_data->method_signals[j]; j++)
495 g_signal_handler_disconnect (interface, skeleton_data->method_signals[j]);
497 g_free (skeleton_data->prop_bindings);
498 g_free (skeleton_data->method_signals);
499 g_slice_free (SkeletonData, skeleton_data);
501 g_object_unref (interface);
505 nm_exported_object_destroy_skeletons (NMExportedObject *self)
507 NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
509 g_return_if_fail (priv->interfaces);
511 while (priv->interfaces) {
512 GDBusInterfaceSkeleton *interface = priv->interfaces->data;
514 priv->interfaces = g_slist_delete_link (priv->interfaces, priv->interfaces);
515 g_dbus_object_skeleton_remove_interface ((GDBusObjectSkeleton *) self, interface);
516 nm_exported_object_skeleton_release (interface);
521 * nm_exported_object_export:
522 * @self: an #NMExportedObject
524 * Exports @self on all active and future D-Bus connections.
526 * The path to export @self on is taken from its #NMObjectClass's %export_path
527 * member. If the %export_path contains "%u", then it will be replaced with a
528 * monotonically increasing integer ID (with each distinct %export_path having
529 * its own counter). Otherwise, %export_path will be used literally (implying
530 * that @self must be a singleton).
532 * Returns: the path @self was exported under
535 nm_exported_object_export (NMExportedObject *self)
537 NMExportedObjectPrivate *priv;
538 const char *class_export_path, *p;
542 g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL);
543 priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
545 g_return_val_if_fail (!priv->path, priv->path);
546 g_return_val_if_fail (!priv->bus_mgr, priv->path);
548 #ifdef _ASSERT_NO_EARLY_EXPORT
549 nm_assert (priv->_constructed);
552 priv->bus_mgr = nm_bus_manager_get ();
554 g_return_val_if_reached (NULL);
555 g_object_add_weak_pointer ((GObject *) priv->bus_mgr, (gpointer *) &priv->bus_mgr);
557 class_export_path = NM_EXPORTED_OBJECT_GET_CLASS (self)->export_path;
558 p = strchr (class_export_path, '%');
562 g_return_val_if_fail (p[1] == 'u', NULL);
563 g_return_val_if_fail (strchr (p + 1, '%') == NULL, NULL);
565 counter = g_hash_table_lookup (prefix_counters, class_export_path);
567 counter = g_new0 (guint, 1);
568 g_hash_table_insert (prefix_counters, g_strdup (class_export_path), counter);
571 path = g_strdup_printf (class_export_path, (*counter)++);
573 path = g_strdup (class_export_path);
575 type = G_OBJECT_TYPE (self);
576 while (type != NM_TYPE_EXPORTED_OBJECT) {
577 nm_exported_object_create_skeletons (self, type);
578 type = g_type_parent (type);
582 _LOGT ("export: \"%s\"", priv->path);
583 g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (self), priv->path);
585 /* Important: priv->path and priv->interfaces must not change while
586 * the object is registered. */
588 nm_bus_manager_register_object (priv->bus_mgr, (GDBusObjectSkeleton *) self);
594 * nm_exported_object_get_path:
595 * @self: an #NMExportedObject
597 * Gets @self's D-Bus path.
599 * Returns: @self's D-Bus path, or %NULL if @self is not exported.
602 nm_exported_object_get_path (NMExportedObject *self)
604 g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL);
606 return NM_EXPORTED_OBJECT_GET_PRIVATE (self)->path;
610 * nm_exported_object_is_exported:
611 * @self: an #NMExportedObject
613 * Checks if @self is exported
615 * Returns: %TRUE if @self is exported
618 nm_exported_object_is_exported (NMExportedObject *self)
620 g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), FALSE);
622 return NM_EXPORTED_OBJECT_GET_PRIVATE (self)->path != NULL;
626 * nm_exported_object_unexport:
627 * @self: an #NMExportedObject
629 * Unexports @self on all active D-Bus connections (and prevents it from being
630 * auto-exported on future connections).
633 nm_exported_object_unexport (NMExportedObject *self)
635 NMExportedObjectPrivate *priv;
637 g_return_if_fail (NM_IS_EXPORTED_OBJECT (self));
638 priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
640 g_return_if_fail (priv->path);
642 /* Important: priv->path and priv->interfaces must not change while
643 * the object is registered. */
645 _LOGT ("unexport: \"%s\"", priv->path);
648 nm_bus_manager_unregister_object (priv->bus_mgr, (GDBusObjectSkeleton *) self);
649 g_object_remove_weak_pointer ((GObject *) priv->bus_mgr, (gpointer *) &priv->bus_mgr);
650 priv->bus_mgr = NULL;
653 nm_exported_object_destroy_skeletons (self);
655 g_dbus_object_skeleton_set_object_path ((GDBusObjectSkeleton *) self, NULL);
657 g_clear_pointer (&priv->path, g_free);
659 if (nm_clear_g_source (&priv->notify_idle_id)) {
660 /* We had a notification queued. Since we removed all interfaces,
661 * the notification is obsolete and must be cleaned up. */
662 g_hash_table_remove_all (priv->pending_notifies);
667 nm_exported_object_get_interfaces (NMExportedObject *self)
669 NMExportedObjectPrivate *priv;
671 g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL);
673 priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
675 g_return_val_if_fail (priv->path, NULL);
676 g_return_val_if_fail (priv->interfaces, NULL);
678 return priv->interfaces;
681 GDBusInterfaceSkeleton *
682 nm_exported_object_get_interface_by_type (NMExportedObject *self, GType interface_type)
686 interfaces = nm_exported_object_get_interfaces (self);
687 for (; interfaces; interfaces = interfaces->next) {
688 if (G_TYPE_CHECK_INSTANCE_TYPE (interfaces->data, interface_type))
689 return interfaces->data;
695 _nm_exported_object_clear_and_unexport (NMExportedObject **location)
697 NMExportedObject *self;
698 NMExportedObjectPrivate *priv;
700 if (!location || !*location)
706 g_return_if_fail (NM_IS_EXPORTED_OBJECT (self));
708 priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
711 nm_exported_object_unexport (self);
713 g_object_unref (self);
717 nm_exported_object_init (NMExportedObject *self)
719 NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
721 priv->pending_notifies = g_hash_table_new_full (g_direct_hash,
724 (GDestroyNotify) g_variant_unref);
728 idle_emit_properties_changed (gpointer self)
730 NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
733 GDBusInterfaceSkeleton *interface = NULL;
735 GHashTableIter hash_iter;
736 const char *dbus_property_name;
737 GVariantBuilder notifies;
739 priv->notify_idle_id = 0;
741 g_variant_builder_init (¬ifies, G_VARIANT_TYPE_VARDICT);
742 g_hash_table_iter_init (&hash_iter, priv->pending_notifies);
743 while (g_hash_table_iter_next (&hash_iter, (gpointer) &dbus_property_name, (gpointer) &variant))
744 g_variant_builder_add (¬ifies, "{sv}", dbus_property_name, variant);
745 g_hash_table_remove_all (priv->pending_notifies);
746 variant = g_variant_builder_end (¬ifies);
747 g_variant_ref_sink (variant);
749 for (iter = priv->interfaces; iter; iter = iter->next) {
750 signal_id = g_signal_lookup ("properties-changed", G_OBJECT_TYPE (iter->data));
751 if (signal_id != 0) {
752 interface = G_DBUS_INTERFACE_SKELETON (iter->data);
756 g_return_val_if_fail (signal_id != 0, FALSE);
758 if (nm_logging_enabled (LOGL_DEBUG, LOGD_DBUS_PROPS)) {
759 gs_free char *notification = g_variant_print (variant, TRUE);
761 nm_log_dbg (LOGD_DBUS_PROPS, "PropertiesChanged %s %p: %s",
762 G_OBJECT_TYPE_NAME (self), self, notification);
765 g_signal_emit (interface, signal_id, 0, variant);
766 g_variant_unref (variant);
771 static const GVariantType *
772 find_dbus_property_type (GDBusInterfaceSkeleton *skel,
773 const char *dbus_property_name)
775 GDBusInterfaceInfo *iinfo;
778 iinfo = g_dbus_interface_skeleton_get_info (skel);
779 for (i = 0; iinfo->properties[i]; i++) {
780 if (!strcmp (iinfo->properties[i]->name, dbus_property_name))
781 return G_VARIANT_TYPE (iinfo->properties[i]->signature);
788 nm_exported_object_notify (GObject *object, GParamSpec *pspec)
790 NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (object);
791 NMExportedObjectClassInfo *classinfo;
793 const char *dbus_property_name = NULL;
794 GValue value = G_VALUE_INIT;
795 const GVariantType *vtype;
799 if (!priv->interfaces)
802 for (type = G_OBJECT_TYPE (object); type; type = g_type_parent (type)) {
803 classinfo = g_type_get_qdata (type, nm_exported_object_class_info_quark ());
807 dbus_property_name = g_hash_table_lookup (classinfo->properties, pspec->name);
808 if (dbus_property_name)
811 if (!dbus_property_name) {
812 nm_log_trace (LOGD_DBUS_PROPS, "ignoring notification for prop %s on type %s",
813 pspec->name, G_OBJECT_TYPE_NAME (object));
817 g_value_init (&value, pspec->value_type);
818 g_object_get_property (G_OBJECT (object), pspec->name, &value);
821 for (iter = priv->interfaces; iter && !vtype; iter = iter->next)
822 vtype = find_dbus_property_type (iter->data, dbus_property_name);
823 g_return_if_fail (vtype != NULL);
825 variant = g_dbus_gvalue_to_gvariant (&value, vtype);
826 /* @dbus_property_name is inside classinfo and never freed, thus we don't clone it.
827 * Also, we do a pointer, not string comparison. */
828 g_hash_table_insert (priv->pending_notifies, (gpointer) dbus_property_name, variant);
829 g_value_unset (&value);
831 if (!priv->notify_idle_id)
832 priv->notify_idle_id = g_idle_add (idle_emit_properties_changed, object);
836 constructed (GObject *object)
838 NMExportedObjectClass *klass;
840 G_OBJECT_CLASS (nm_exported_object_parent_class)->constructed (object);
842 #ifdef _ASSERT_NO_EARLY_EXPORT
843 NM_EXPORTED_OBJECT_GET_PRIVATE (object)->_constructed = TRUE;
846 klass = NM_EXPORTED_OBJECT_GET_CLASS (object);
848 if (klass->export_on_construction)
849 nm_exported_object_export ((NMExportedObject *) object);
853 nm_exported_object_dispose (GObject *object)
855 NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (object);
857 /* Objects should have already been unexported by their owner, unless
858 * we are quitting, where many objects stick around until exit.
862 g_warn_if_reached ();
863 nm_exported_object_unexport (NM_EXPORTED_OBJECT (object));
866 g_clear_pointer (&priv->path, g_free);
868 g_clear_pointer (&priv->pending_notifies, g_hash_table_destroy);
869 nm_clear_g_source (&priv->notify_idle_id);
871 G_OBJECT_CLASS (nm_exported_object_parent_class)->dispose (object);
875 nm_exported_object_class_init (NMExportedObjectClass *klass)
877 GObjectClass *object_class = G_OBJECT_CLASS (klass);
879 g_type_class_add_private (object_class, sizeof (NMExportedObjectPrivate));
881 object_class->constructed = constructed;
882 object_class->notify = nm_exported_object_notify;
883 object_class->dispose = nm_exported_object_dispose;
887 nm_exported_object_class_set_quitting (void)