device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm-glib / nm-object.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  * Boston, MA 02110-1301 USA.
17  *
18  * Copyright 2007 - 2008 Novell, Inc.
19  * Copyright 2007 - 2012 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 #include "nm-utils.h"
29 #include "NetworkManager.h"
30 #include "nm-object.h"
31 #include "nm-object-cache.h"
32 #include "nm-object-private.h"
33 #include "nm-dbus-glib-types.h"
34 #include "nm-types.h"
35 #include "nm-dbus-helpers-private.h"
36
37 static gboolean debug = FALSE;
38 #define dbgmsg(f,...) if (G_UNLIKELY (debug)) { g_message (f, ## __VA_ARGS__ ); }
39
40 static void nm_object_initable_iface_init (GInitableIface *iface);
41 static void nm_object_async_initable_iface_init (GAsyncInitableIface *iface);
42
43 static GHashTable *type_funcs, *type_async_funcs;
44
45 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMObject, nm_object, G_TYPE_OBJECT,
46                                   type_funcs = g_hash_table_new (NULL, NULL);
47                                   type_async_funcs = g_hash_table_new (NULL, NULL);
48                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_object_initable_iface_init);
49                                   G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_object_async_initable_iface_init);
50                                   )
51
52 #define NM_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OBJECT, NMObjectPrivate))
53
54 typedef struct {
55         PropertyMarshalFunc func;
56         GType object_type;
57         gpointer field;
58         const char *signal_prefix;
59 } PropertyInfo;
60
61 static void reload_complete (NMObject *object, gboolean emit_now);
62
63 typedef struct {
64         DBusGConnection *connection;
65         DBusGProxy *bus_proxy;
66         gboolean nm_running;
67
68         char *path;
69         DBusGProxy *properties_proxy;
70         GSList *property_interfaces;
71         GSList *property_tables;
72         NMObject *parent;
73         gboolean suppress_property_updates;
74
75         GSList *notify_items;
76         guint32 notify_id;
77         gboolean inited;
78
79         GSList *reload_results;
80         guint reload_remaining;
81         GError *reload_error;
82 } NMObjectPrivate;
83
84 enum {
85         PROP_0,
86         PROP_DBUS_CONNECTION,
87         PROP_DBUS_PATH,
88
89         LAST_PROP
90 };
91
92 enum {
93         OBJECT_CREATION_FAILED,
94
95         LAST_SIGNAL
96 };
97
98 static guint signals[LAST_SIGNAL] = { 0 };
99
100 /**
101  * nm_object_error_quark:
102  *
103  * Registers an error quark for #NMObject if necessary.
104  *
105  * Returns: the error quark used for #NMObject errors.
106  **/
107 GQuark
108 nm_object_error_quark (void)
109 {
110         static GQuark quark;
111
112         if (G_UNLIKELY (!quark))
113                 quark = g_quark_from_static_string ("nm-object-error-quark");
114         return quark;
115 }
116
117 typedef enum {
118         NOTIFY_SIGNAL_PENDING_NONE,
119         NOTIFY_SIGNAL_PENDING_ADDED,
120         NOTIFY_SIGNAL_PENDING_REMOVED,
121         NOTIFY_SIGNAL_PENDING_ADDED_REMOVED,
122 } NotifySignalPending;
123
124 typedef struct {
125         const char *property;
126         const char *signal_prefix;
127         NotifySignalPending pending;
128         NMObject *changed;
129 } NotifyItem;
130
131 static void
132 notify_item_free (NotifyItem *item)
133 {
134         g_clear_object (&item->changed);
135         g_slice_free (NotifyItem, item);
136 }
137
138 static void
139 proxy_name_owner_changed (DBusGProxy *proxy,
140                           const char *name,
141                           const char *old_owner,
142                           const char *new_owner,
143                           gpointer user_data)
144 {
145         NMObject *self = NM_OBJECT (user_data);
146         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
147
148         if (g_strcmp0 (name, NM_DBUS_SERVICE) == 0) {
149                 gboolean old_good = (old_owner && old_owner[0]);
150                 gboolean new_good = (new_owner && new_owner[0]);
151
152                 if (!old_good && new_good)
153                         priv->nm_running = TRUE;
154                 else if (old_good && !new_good)
155                         priv->nm_running = FALSE;
156         }
157 }
158
159 static void
160 nm_object_init (NMObject *object)
161 {
162 }
163
164 static GObject*
165 constructor (GType type,
166              guint n_construct_params,
167              GObjectConstructParam *construct_params)
168 {
169         GObject *object;
170         NMObjectPrivate *priv;
171
172         object = G_OBJECT_CLASS (nm_object_parent_class)->constructor (type,
173                                                                        n_construct_params,
174                                                                        construct_params);
175
176         priv = NM_OBJECT_GET_PRIVATE (object);
177
178         if (priv->connection == NULL || priv->path == NULL) {
179                 g_warn_if_reached ();
180                 g_object_unref (object);
181                 return NULL;
182         }
183
184         return object;
185 }
186
187 static void
188 constructed (GObject *object)
189 {
190         NMObject *self = NM_OBJECT (object);
191         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
192
193         if (G_OBJECT_CLASS (nm_object_parent_class)->constructed)
194                 G_OBJECT_CLASS (nm_object_parent_class)->constructed (object);
195
196         priv->properties_proxy = _nm_object_new_proxy (self, NULL, DBUS_INTERFACE_PROPERTIES);
197
198         priv->bus_proxy = dbus_g_proxy_new_for_name (priv->connection,
199                                                      DBUS_SERVICE_DBUS,
200                                                      DBUS_PATH_DBUS,
201                                                      DBUS_INTERFACE_DBUS);
202         g_assert (priv->bus_proxy);
203
204         dbus_g_proxy_add_signal (priv->bus_proxy, "NameOwnerChanged",
205                                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
206                                  G_TYPE_INVALID);
207         dbus_g_proxy_connect_signal (priv->bus_proxy,
208                                      "NameOwnerChanged",
209                                      G_CALLBACK (proxy_name_owner_changed),
210                                      object, NULL);
211 }
212
213 static gboolean
214 init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
215 {
216         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable);
217
218         if (priv->bus_proxy) {
219                 if (!dbus_g_proxy_call (priv->bus_proxy,
220                                         "NameHasOwner", error,
221                                         G_TYPE_STRING, NM_DBUS_SERVICE,
222                                         G_TYPE_INVALID,
223                                         G_TYPE_BOOLEAN, &priv->nm_running,
224                                         G_TYPE_INVALID))
225                         return FALSE;
226         }
227
228         priv->inited = TRUE;
229         return _nm_object_reload_properties (NM_OBJECT (initable), error);
230 }
231
232 /* Takes ownership of @error */
233 static void
234 init_async_complete (GSimpleAsyncResult *simple, GError *error)
235 {
236         if (error)
237                 g_simple_async_result_take_error (simple, error);
238         else
239                 g_simple_async_result_set_op_res_gboolean (simple, TRUE);
240         g_simple_async_result_complete (simple);
241         g_object_unref (simple);
242 }
243
244 static void
245 init_async_got_properties (GObject *object, GAsyncResult *result, gpointer user_data)
246 {
247         GSimpleAsyncResult *simple = user_data;
248         GError *error = NULL;
249
250         NM_OBJECT_GET_PRIVATE (object)->inited = TRUE;
251         if (!_nm_object_reload_properties_finish (NM_OBJECT (object), result, &error))
252                 g_assert (error);
253         init_async_complete (simple, error);
254 }
255
256 static void
257 init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call,
258                                 gpointer user_data)
259 {
260         GSimpleAsyncResult *simple = user_data;
261         NMObject *self;
262         NMObjectPrivate *priv;
263         GError *error = NULL;
264
265         self = NM_OBJECT (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
266         priv = NM_OBJECT_GET_PRIVATE (self);
267
268         if (!dbus_g_proxy_end_call (proxy, call, &error,
269                                     G_TYPE_BOOLEAN, &priv->nm_running,
270                                     G_TYPE_INVALID)) {
271                 init_async_complete (simple, error);
272         } else if (!priv->nm_running) {
273                 priv->inited = TRUE;
274                 init_async_complete (simple, NULL);
275         } else
276                 _nm_object_reload_properties_async (self, init_async_got_properties, simple);
277
278         /* g_async_result_get_source_object() adds a ref */
279         g_object_unref (self);
280 }
281
282 static void
283 init_async (GAsyncInitable *initable, int io_priority,
284             GCancellable *cancellable, GAsyncReadyCallback callback,
285             gpointer user_data)
286 {
287         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable);
288         GSimpleAsyncResult *simple;
289
290         simple = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, init_async);
291
292         /* Check if NM is running */
293         dbus_g_proxy_begin_call (priv->bus_proxy, "NameHasOwner",
294                                  init_async_got_manager_running,
295                                  simple, NULL,
296                                  G_TYPE_STRING, NM_DBUS_SERVICE,
297                                  G_TYPE_INVALID);
298 }
299
300 static gboolean
301 init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
302 {
303         GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
304
305         if (g_simple_async_result_propagate_error (simple, error))
306                 return FALSE;
307         else
308                 return TRUE;
309 }
310
311 static void
312 dispose (GObject *object)
313 {
314         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
315
316         if (priv->notify_id) {
317                 g_source_remove (priv->notify_id);
318                 priv->notify_id = 0;
319         }
320
321         g_slist_free_full (priv->notify_items, (GDestroyNotify) notify_item_free);
322         priv->notify_items = NULL;
323
324         g_slist_free_full (priv->property_interfaces, g_free);
325         priv->property_interfaces = NULL;
326
327         g_clear_object (&priv->properties_proxy);
328         g_clear_object (&priv->bus_proxy);
329
330         if (priv->connection) {
331                 dbus_g_connection_unref (priv->connection);
332                 priv->connection = NULL;
333         }
334
335         G_OBJECT_CLASS (nm_object_parent_class)->dispose (object);
336 }
337
338 static void
339 finalize (GObject *object)
340 {
341         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
342
343         g_slist_free_full (priv->property_tables, (GDestroyNotify) g_hash_table_destroy);
344         g_free (priv->path);
345
346         G_OBJECT_CLASS (nm_object_parent_class)->finalize (object);
347 }
348
349 static void
350 set_property (GObject *object, guint prop_id,
351               const GValue *value, GParamSpec *pspec)
352 {
353         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
354
355         switch (prop_id) {
356         case PROP_DBUS_CONNECTION:
357                 /* Construct only */
358                 priv->connection = g_value_dup_boxed (value);
359                 if (!priv->connection)
360                         priv->connection = _nm_dbus_new_connection (NULL);
361                 break;
362         case PROP_DBUS_PATH:
363                 /* Construct only */
364                 priv->path = g_value_dup_string (value);
365                 break;
366         default:
367                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368                 break;
369         }
370 }
371
372 static void
373 get_property (GObject *object, guint prop_id,
374               GValue *value, GParamSpec *pspec)
375 {
376         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
377
378         switch (prop_id) {
379         case PROP_DBUS_CONNECTION:
380                 g_value_set_boxed (value, priv->connection);
381                 break;
382         case PROP_DBUS_PATH:
383                 g_value_set_string (value, priv->path);
384                 break;
385         default:
386                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387                 break;
388         }
389 }
390
391 static void
392 nm_object_class_init (NMObjectClass *nm_object_class)
393 {
394         GObjectClass *object_class = G_OBJECT_CLASS (nm_object_class);
395
396         g_type_class_add_private (nm_object_class, sizeof (NMObjectPrivate));
397
398         /* virtual methods */
399         object_class->constructor = constructor;
400         object_class->constructed = constructed;
401         object_class->set_property = set_property;
402         object_class->get_property = get_property;
403         object_class->dispose = dispose;
404         object_class->finalize = finalize;
405
406         /* Properties */
407
408         /**
409          * NMObject:connection:
410          *
411          * The #DBusGConnection of the object.
412          **/
413         g_object_class_install_property
414                 (object_class, PROP_DBUS_CONNECTION,
415                  g_param_spec_boxed (NM_OBJECT_DBUS_CONNECTION, "", "",
416                                      DBUS_TYPE_G_CONNECTION,
417                                      G_PARAM_READWRITE |
418                                      G_PARAM_CONSTRUCT_ONLY |
419                                      G_PARAM_STATIC_STRINGS));
420
421         /**
422          * NMObject:path:
423          *
424          * The DBus object path.
425          **/
426         g_object_class_install_property
427                 (object_class, PROP_DBUS_PATH,
428                  g_param_spec_string (NM_OBJECT_DBUS_PATH, "", "",
429                                       NULL,
430                                       G_PARAM_READWRITE |
431                                       G_PARAM_CONSTRUCT_ONLY |
432                                       G_PARAM_STATIC_STRINGS));
433
434         /* signals */
435
436         /**
437          * NMObject::object-creation-failed:
438          * @master_object: the object that received the signal
439          * @error: the error that occured while creating object
440          * @failed_path: object path of the failed object
441          *
442          * Indicates that an error occured while creating an #NMObject object
443          * during property handling of @master_object.
444          *
445          * Note: Be aware that the signal is private for libnm-glib's internal
446          *       use.
447          **/
448         signals[OBJECT_CREATION_FAILED] =
449                 g_signal_new ("object-creation-failed",
450                               G_OBJECT_CLASS_TYPE (object_class),
451                               G_SIGNAL_RUN_FIRST,
452                               G_STRUCT_OFFSET (NMObjectClass, object_creation_failed),
453                               NULL, NULL, NULL,
454                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
455 }
456
457 static void
458 nm_object_initable_iface_init (GInitableIface *iface)
459 {
460         iface->init = init_sync;
461 }
462
463 static void
464 nm_object_async_initable_iface_init (GAsyncInitableIface *iface)
465 {
466         iface->init_async = init_async;
467         iface->init_finish = init_finish;
468 }
469
470 /**
471  * nm_object_get_connection:
472  * @object: a #NMObject
473  *
474  * Gets the #NMObject's DBusGConnection.
475  *
476  * Returns: (transfer none): the connection
477  **/
478 DBusGConnection *
479 nm_object_get_connection (NMObject *object)
480 {
481         g_return_val_if_fail (NM_IS_OBJECT (object), NULL);
482
483         return NM_OBJECT_GET_PRIVATE (object)->connection;
484 }
485
486 /**
487  * nm_object_get_path:
488  * @object: a #NMObject
489  *
490  * Gets the DBus path of the #NMObject.
491  *
492  * Returns: the object's path. This is the internal string used by the
493  * device, and must not be modified.
494  **/
495 const char *
496 nm_object_get_path (NMObject *object)
497 {
498         g_return_val_if_fail (NM_IS_OBJECT (object), NULL);
499
500         return NM_OBJECT_GET_PRIVATE (object)->path;
501 }
502
503 static gboolean
504 deferred_notify_cb (gpointer data)
505 {
506         NMObject *object = NM_OBJECT (data);
507         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
508         NMObjectClass *object_class = NM_OBJECT_GET_CLASS (object);
509         GSList *props, *iter;
510
511         priv->notify_id = 0;
512
513         /* Wait until all reloads are done before notifying */
514         if (priv->reload_remaining)
515                 return G_SOURCE_REMOVE;
516
517         /* Clear priv->notify_items early so that an NMObject subclass that
518          * listens to property changes can queue up other property changes
519          * during the g_object_notify() call separately from the property
520          * list we're iterating.
521          */
522         props = g_slist_reverse (priv->notify_items);
523         priv->notify_items = NULL;
524
525         g_object_ref (object);
526
527         /* Emit property change notifications first */
528         for (iter = props; iter; iter = g_slist_next (iter)) {
529                 NotifyItem *item = iter->data;
530
531                 if (item->property)
532                         g_object_notify (G_OBJECT (object), item->property);
533         }
534
535         /* And added/removed signals second */
536         for (iter = props; iter; iter = g_slist_next (iter)) {
537                 NotifyItem *item = iter->data;
538                 char buf[50];
539                 gint ret = 0;
540
541                 switch (item->pending) {
542                 case NOTIFY_SIGNAL_PENDING_ADDED:
543                         ret = g_snprintf (buf, sizeof (buf), "%s-added", item->signal_prefix);
544                         break;
545                 case NOTIFY_SIGNAL_PENDING_REMOVED:
546                         ret = g_snprintf (buf, sizeof (buf), "%s-removed", item->signal_prefix);
547                         break;
548                 case NOTIFY_SIGNAL_PENDING_ADDED_REMOVED:
549                         // XXX
550                         if (object_class->object_creation_failed)
551                                 object_class->object_creation_failed (object, NULL, g_strdup (nm_object_get_path (item->changed)));
552                         break;
553                 case NOTIFY_SIGNAL_PENDING_NONE:
554                 default:
555                         break;
556                 }
557                 if (ret > 0) {
558                         g_assert (ret < sizeof (buf));
559                         g_signal_emit_by_name (object, buf, item->changed);
560                 }
561         }
562         g_object_unref (object);
563
564         g_slist_free_full (props, (GDestroyNotify) notify_item_free);
565         return G_SOURCE_REMOVE;
566 }
567
568 static void
569 _nm_object_defer_notify (NMObject *object)
570 {
571         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
572
573         if (!priv->notify_id)
574                 priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL);
575 }
576
577 static void
578 _nm_object_queue_notify_full (NMObject *object,
579                               const char *property,
580                               const char *signal_prefix,
581                               gboolean added,
582                               NMObject *changed)
583 {
584         NMObjectPrivate *priv;
585         NotifyItem *item;
586         GSList *iter;
587
588         g_return_if_fail (NM_IS_OBJECT (object));
589         g_return_if_fail (!signal_prefix != !property);
590         g_return_if_fail (!signal_prefix == !changed);
591
592         priv = NM_OBJECT_GET_PRIVATE (object);
593         _nm_object_defer_notify (object);
594
595         property = g_intern_string (property);
596         signal_prefix = g_intern_string (signal_prefix);
597         for (iter = priv->notify_items; iter; iter = g_slist_next (iter)) {
598                 item = iter->data;
599
600                 if (property && (property == item->property))
601                         return;
602
603                 /* Collapse signals for the same object (such as "added->removed") to
604                  * ensure we don't emit signals when their sum should have no effect.
605                  * The "added->removed->removed" sequence requires special handling,
606                  * hence the addition of the ADDED_REMOVED state to ensure that no
607                  * signal is emitted in this case:
608                  *
609                  * Without the ADDED_REMOVED state:
610                  *     NONE          + added   -> ADDED
611                  *     ADDED         + removed -> NONE
612                  *     NONE          + removed -> REMOVED (would emit 'removed' signal)
613                  *
614                  * With the ADDED_REMOVED state:
615                  *     NONE | ADDED_REMOVED  + added   -> ADDED
616                  *     ADDED                 + removed -> ADDED_REMOVED
617                  *     ADDED_REMOVED         + removed -> ADDED_REMOVED (emits no signal)
618                  */
619                 if (signal_prefix && (changed == item->changed) && (item->signal_prefix == signal_prefix)) {
620                         switch (item->pending) {
621                         case NOTIFY_SIGNAL_PENDING_ADDED:
622                                 if (!added)
623                                         item->pending = NOTIFY_SIGNAL_PENDING_ADDED_REMOVED;
624                                 break;
625                         case NOTIFY_SIGNAL_PENDING_REMOVED:
626                                 if (added)
627                                         item->pending = NOTIFY_SIGNAL_PENDING_NONE;
628                                 break;
629                         case NOTIFY_SIGNAL_PENDING_ADDED_REMOVED:
630                                 if (added)
631                                         item->pending = NOTIFY_SIGNAL_PENDING_ADDED;
632                                 break;
633                         case NOTIFY_SIGNAL_PENDING_NONE:
634                                 item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED;
635                                 break;
636                         default:
637                                 g_assert_not_reached ();
638                         }
639                         return;
640                 }
641         }
642
643         item = g_slice_new0 (NotifyItem);
644         item->property = property;
645         if (signal_prefix) {
646                 item->signal_prefix = signal_prefix;
647                 item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED;
648                 item->changed = changed ? g_object_ref (changed) : NULL;
649         }
650         priv->notify_items = g_slist_prepend (priv->notify_items, item);
651 }
652
653 void
654 _nm_object_queue_notify (NMObject *object, const char *property)
655 {
656         _nm_object_queue_notify_full (object, property, NULL, FALSE, NULL);
657 }
658
659 void
660 _nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func,
661                                NMObjectTypeAsyncFunc type_async_func)
662 {
663         g_hash_table_insert (type_funcs,
664                              GSIZE_TO_POINTER (base_type),
665                              type_func);
666         g_hash_table_insert (type_async_funcs,
667                              GSIZE_TO_POINTER (base_type),
668                              type_async_func);
669 }
670
671 static GObject *
672 _nm_object_create (GType type, DBusGConnection *connection, const char *path)
673 {
674         NMObjectTypeFunc type_func;
675         GObject *object;
676         GError *error = NULL;
677
678         type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type));
679         if (type_func)
680                 type = type_func (connection, path);
681
682         if (type == G_TYPE_INVALID) {
683                 dbgmsg ("Could not create object for %s: unknown object type", path);
684                 return NULL;
685         }
686
687         object = g_object_new (type,
688                                NM_OBJECT_DBUS_CONNECTION, connection,
689                                NM_OBJECT_DBUS_PATH, path,
690                                NULL);
691         if (NM_IS_OBJECT (object))
692                 _nm_object_cache_add (NM_OBJECT (object));
693         if (!g_initable_init (G_INITABLE (object), NULL, &error)) {
694                 dbgmsg ("Could not create object for %s: %s", path, error->message);
695                 g_error_free (error);
696                 g_clear_object (&object);
697         }
698
699         return object;
700 }
701
702 typedef void (*NMObjectCreateCallbackFunc) (GObject *, const char *, gpointer);
703 typedef struct {
704         DBusGConnection *connection;
705         char *path;
706         NMObjectCreateCallbackFunc callback;
707         gpointer user_data;
708 } NMObjectTypeAsyncData;
709
710 static void
711 create_async_complete (GObject *object, NMObjectTypeAsyncData *async_data)
712 {
713         async_data->callback (object, async_data->path, async_data->user_data);
714
715         g_free (async_data->path);
716         g_slice_free (NMObjectTypeAsyncData, async_data);
717 }
718
719 static const char *
720 nm_object_or_connection_get_path (gpointer instance)
721 {
722         if (NM_IS_OBJECT (instance))
723                 return nm_object_get_path (instance);
724         else if (NM_IS_CONNECTION (instance))
725                 return nm_connection_get_path (instance);
726
727         g_assert_not_reached ();
728 }
729
730 static void
731 async_inited (GObject *source, GAsyncResult *result, gpointer user_data)
732 {
733         NMObjectTypeAsyncData *async_data = user_data;
734         GObject *object = G_OBJECT (source);
735         GError *error = NULL;
736
737         if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error)) {
738                 dbgmsg ("Could not create object for %s: %s",
739                         nm_object_or_connection_get_path (object),
740                         error->message);
741                 g_error_free (error);
742                 g_clear_object (&object);
743         }
744
745         create_async_complete (object, async_data);
746 }
747
748 static void
749 async_got_type (GType type, gpointer user_data)
750 {
751         NMObjectTypeAsyncData *async_data = user_data;
752         GObject *object;
753
754         /* Ensure we don't have the object already; we may get multiple type
755          * requests for the same object if there are multiple properties on
756          * other objects that refer to the object at this path.  One of those
757          * other requests may have already completed.
758          */
759         object = (GObject *) _nm_object_cache_get (async_data->path);
760         if (object) {
761                 create_async_complete (object, async_data);
762                 return;
763         }
764
765         if (type == G_TYPE_INVALID) {
766                 /* Don't know how to create this object */
767                 create_async_complete (NULL, async_data);
768                 return;
769         }
770
771         object = g_object_new (type,
772                                NM_OBJECT_DBUS_CONNECTION, async_data->connection,
773                                NM_OBJECT_DBUS_PATH, async_data->path,
774                                NULL);
775         g_warn_if_fail (object != NULL);
776         if (NM_IS_OBJECT (object))
777                 _nm_object_cache_add (NM_OBJECT (object));
778         g_async_initable_init_async (G_ASYNC_INITABLE (object), G_PRIORITY_DEFAULT,
779                                      NULL, async_inited, async_data);
780 }
781
782 static void
783 _nm_object_create_async (GType type, DBusGConnection *connection, const char *path,
784                          NMObjectCreateCallbackFunc callback, gpointer user_data)
785 {
786         NMObjectTypeAsyncFunc type_async_func;
787         NMObjectTypeFunc type_func;
788         NMObjectTypeAsyncData *async_data;
789
790         async_data = g_slice_new (NMObjectTypeAsyncData);
791         async_data->connection = connection;
792         async_data->path = g_strdup (path);
793         async_data->callback = callback;
794         async_data->user_data = user_data;
795
796         type_async_func = g_hash_table_lookup (type_async_funcs, GSIZE_TO_POINTER (type));
797         if (type_async_func) {
798                 type_async_func (connection, path, async_got_type, async_data);
799                 return;
800         }
801
802         type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type));
803         if (type_func)
804                 type = type_func (connection, path);
805
806         async_got_type (type, async_data);
807 }
808
809 /* Stolen from dbus-glib */
810 static char*
811 wincaps_to_dash (const char *caps)
812 {
813         const char *p;
814         GString *str;
815
816         str = g_string_new (NULL);
817         p = caps;
818         while (*p) {
819                 if (g_ascii_isupper (*p)) {
820                         if (str->len > 0 && (str->len < 2 || str->str[str->len-2] != '-'))
821                                 g_string_append_c (str, '-');
822                         g_string_append_c (str, g_ascii_tolower (*p));
823                 } else
824                         g_string_append_c (str, *p);
825                 ++p;
826         }
827
828         return g_string_free (str, FALSE);
829 }
830
831 /* Adds object to array if it's not already there */
832 static void
833 add_to_object_array_unique (GPtrArray *array, GObject *obj)
834 {
835         guint i;
836
837         g_return_if_fail (array != NULL);
838
839         if (obj != NULL) {
840                 for (i = 0; i < array->len; i++) {
841                         if (g_ptr_array_index (array, i) == obj) {
842                                 g_object_unref (obj);
843                                 return;
844                         }
845                 }
846                 g_ptr_array_add (array, obj);
847         }
848 }
849
850 typedef struct {
851         NMObject *self;
852         PropertyInfo *pi;
853
854         GObject **objects;
855         int length, remaining;
856
857         gboolean array;
858         const char *property_name;
859 } ObjectCreatedData;
860
861 /* Places items from 'needles' that are not in 'haystack' into 'diff' */
862 static void
863 array_diff (GPtrArray *needles, GPtrArray *haystack, GPtrArray *diff)
864 {
865         guint i, j;
866         GObject *obj;
867
868         g_assert (needles);
869         g_assert (haystack);
870         g_assert (diff);
871
872         for (i = 0; i < needles->len; i++) {
873                 obj = g_ptr_array_index (needles, i);
874
875                 for (j = 0; j < haystack->len; j++) {
876                         if (g_ptr_array_index (haystack, j) == obj)
877                                 break;
878                 }
879
880                 if (j == haystack->len)
881                         g_ptr_array_add (diff, obj);
882         }
883 }
884
885 static void
886 queue_added_removed_signal (NMObject *self,
887                             const char *signal_prefix,
888                             NMObject *changed,
889                             gboolean added)
890 {
891         _nm_object_queue_notify_full (self, NULL, signal_prefix, added, changed);
892 }
893
894 static void
895 object_property_complete (ObjectCreatedData *odata)
896 {
897         NMObject *self = odata->self;
898         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
899         PropertyInfo *pi = odata->pi;
900         gboolean different = TRUE;
901
902         if (odata->array) {
903                 GPtrArray *old = *((GPtrArray **) pi->field);
904                 GPtrArray *new;
905                 int i;
906
907                 /* Build up new array */
908                 new = g_ptr_array_sized_new (odata->length);
909                 for (i = 0; i < odata->length; i++)
910                         add_to_object_array_unique (new, odata->objects[i]);
911
912                 if (pi->signal_prefix) {
913                         GPtrArray *added = g_ptr_array_sized_new (3);
914                         GPtrArray *removed = g_ptr_array_sized_new (3);
915
916                         if (old) {
917                                 /* Find objects in 'old' that do not exist in 'new' */
918                                 array_diff (old, new, removed);
919
920                                 /* Find objects in 'new' that do not exist in old */
921                                 array_diff (new, old, added);
922                         } else {
923                                 for (i = 0; i < new->len; i++)
924                                         g_ptr_array_add (added, g_ptr_array_index (new, i));
925                         }
926
927                         *((GPtrArray **) pi->field) = new;
928
929                         /* Emit added & removed */
930                         for (i = 0; i < removed->len; i++) {
931                                 queue_added_removed_signal (self,
932                                                             pi->signal_prefix,
933                                                             g_ptr_array_index (removed, i),
934                                                             FALSE);
935                         }
936
937                         for (i = 0; i < added->len; i++) {
938                                 queue_added_removed_signal (self,
939                                                             pi->signal_prefix,
940                                                             g_ptr_array_index (added, i),
941                                                             TRUE);
942                         }
943
944                         different = removed->len || added->len;
945                         g_ptr_array_free (added, TRUE);
946                         g_ptr_array_free (removed, TRUE);
947                 } else {
948                         /* No added/removed signals to send, just replace the property with
949                          * the new values.
950                          */
951                         *((GPtrArray **) pi->field) = new;
952                         different = TRUE;
953                 }
954
955                 /* Free old array last since it will release references, thus freeing
956                  * any objects in the 'removed' array.
957                  */
958                 if (old)
959                         g_boxed_free (NM_TYPE_OBJECT_ARRAY, old);
960         } else {
961                 GObject **obj_p = pi->field;
962
963                 different = (*obj_p != odata->objects[0]);
964                 if (*obj_p)
965                         g_object_unref (*obj_p);
966                 *obj_p = odata->objects[0];
967         }
968
969         if (different && odata->property_name)
970                 _nm_object_queue_notify (self, odata->property_name);
971
972         if (--priv->reload_remaining == 0)
973                 reload_complete (self, FALSE);
974
975         g_object_unref (self);
976         g_free (odata->objects);
977         g_slice_free (ObjectCreatedData, odata);
978 }
979
980 static void
981 object_created (GObject *obj, const char *path, gpointer user_data)
982 {
983         ObjectCreatedData *odata = user_data;
984
985         /* We assume that on error, the creator_func printed something */
986
987         if (obj == NULL && g_strcmp0 (path, "/") != 0 ) {
988                 GError *error;
989                 error = g_error_new (NM_OBJECT_ERROR,
990                                      NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE,
991                                      "Creating object for path '%s' failed in libnm-glib.",
992                                      path);
993                 /* Emit a signal about the error. */
994                 g_signal_emit (odata->self, signals[OBJECT_CREATION_FAILED], 0, error, path);
995                 g_error_free (error);
996         }
997
998         odata->objects[--odata->remaining] = obj;
999         if (!odata->remaining)
1000                 object_property_complete (odata);
1001 }
1002
1003 static gboolean
1004 handle_object_property (NMObject *self, const char *property_name, GValue *value,
1005                         PropertyInfo *pi, gboolean synchronously)
1006 {
1007         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
1008         GObject *obj;
1009         const char *path;
1010         ObjectCreatedData *odata;
1011
1012         odata = g_slice_new (ObjectCreatedData);
1013         odata->self = g_object_ref (self);
1014         odata->pi = pi;
1015         odata->objects = g_new (GObject *, 1);
1016         odata->length = odata->remaining = 1;
1017         odata->array = FALSE;
1018         odata->property_name = property_name;
1019
1020         priv->reload_remaining++;
1021
1022         path = g_value_get_boxed (value);
1023
1024         if (!strcmp (path, "/")) {
1025                 object_created (NULL, path, odata);
1026                 return TRUE;
1027         }
1028
1029         obj = G_OBJECT (_nm_object_cache_get (path));
1030         if (obj) {
1031                 object_created (obj, path, odata);
1032                 return TRUE;
1033         } else if (synchronously) {
1034                 obj = _nm_object_create (pi->object_type, priv->connection, path);
1035                 object_created (obj, path, odata);
1036                 return obj != NULL;
1037         } else {
1038                 _nm_object_create_async (pi->object_type, priv->connection, path,
1039                                          object_created, odata);
1040                 /* Assume success */
1041                 return TRUE;
1042         }
1043 }
1044
1045 static gboolean
1046 handle_object_array_property (NMObject *self, const char *property_name, GValue *value,
1047                               PropertyInfo *pi, gboolean synchronously)
1048 {
1049         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
1050         GObject *obj;
1051         GPtrArray *paths;
1052         GPtrArray **array = pi->field;
1053         const char *path;
1054         ObjectCreatedData *odata;
1055         int i;
1056
1057         paths = g_value_get_boxed (value);
1058
1059         odata = g_slice_new (ObjectCreatedData);
1060         odata->self = g_object_ref (self);
1061         odata->pi = pi;
1062         odata->objects = g_new0 (GObject *, paths->len);
1063         odata->length = odata->remaining = paths->len;
1064         odata->array = TRUE;
1065         odata->property_name = property_name;
1066
1067         priv->reload_remaining++;
1068
1069         if (paths->len == 0) {
1070                 object_property_complete (odata);
1071                 return TRUE;
1072         }
1073
1074         for (i = 0; i < paths->len; i++) {
1075                 path = paths->pdata[i];
1076                 if (!strcmp (path, "/")) {
1077                         /* FIXME: can't happen? */
1078                         continue;
1079                 }
1080
1081                 obj = G_OBJECT (_nm_object_cache_get (path));
1082                 if (obj) {
1083                         object_created (obj, path, odata);
1084                 } else if (synchronously) {
1085                         obj = _nm_object_create (pi->object_type, priv->connection, path);
1086                         object_created (obj, path, odata);
1087                 } else {
1088                         _nm_object_create_async (pi->object_type, priv->connection, path,
1089                                                  object_created, odata);
1090                 }
1091         }
1092
1093         if (!synchronously) {
1094                 /* Assume success */
1095                 return TRUE;
1096         }
1097
1098         return *array && ((*array)->len == paths->len);
1099 }
1100
1101 static void
1102 handle_property_changed (NMObject *self, const char *dbus_name, GValue *value, gboolean synchronously)
1103 {
1104         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
1105         char *prop_name;
1106         PropertyInfo *pi;
1107         GParamSpec *pspec;
1108         gboolean success = FALSE, found = FALSE;
1109         GSList *iter;
1110
1111         prop_name = wincaps_to_dash (dbus_name);
1112
1113         /* Iterate through the object and its parents to find the property */
1114         for (iter = priv->property_tables; iter; iter = g_slist_next (iter)) {
1115                 pi = g_hash_table_lookup ((GHashTable *) iter->data, prop_name);
1116                 if (pi) {
1117                         if (!pi->field) {
1118                                 /* We know about this property but aren't tracking changes on it. */
1119                                 goto out;
1120                         }
1121
1122                         found = TRUE;
1123                         break;
1124                 }
1125         }
1126
1127         if (!found) {
1128                 dbgmsg ("Property '%s' unhandled.", prop_name);
1129                 goto out;
1130         }
1131
1132         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (self)), prop_name);
1133         if (!pspec) {
1134                 dbgmsg ("%s: property '%s' changed but wasn't defined by object type %s.",
1135                         __func__,
1136                         prop_name,
1137                         G_OBJECT_TYPE_NAME (self));
1138                 goto out;
1139         }
1140
1141         if (G_UNLIKELY (debug)) {
1142                 char *s;
1143                 s = g_strdup_value_contents (value);
1144                 dbgmsg ("PC: (%p) %s::%s => '%s' (%s%s%s)",
1145                         self, G_OBJECT_TYPE_NAME (self),
1146                         prop_name,
1147                         s,
1148                         G_VALUE_TYPE_NAME (value),
1149                         pi->object_type ? " / " : "",
1150                         pi->object_type ? g_type_name (pi->object_type) : "");
1151                 g_free (s);
1152         }
1153
1154         if (pi->object_type) {
1155                 if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
1156                         success = handle_object_property (self, pspec->name, value, pi, synchronously);
1157                 else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
1158                         success = handle_object_array_property (self, pspec->name, value, pi, synchronously);
1159                 else {
1160                         g_warn_if_reached ();
1161                         goto out;
1162                 }
1163         } else
1164                 success = (*(pi->func)) (self, pspec, value, pi->field);
1165
1166         if (!success) {
1167                 dbgmsg ("%s: failed to update property '%s' of object type %s.",
1168                         __func__,
1169                         prop_name,
1170                         G_OBJECT_TYPE_NAME (self));
1171         }
1172
1173 out:
1174         g_free (prop_name);
1175 }
1176
1177 static void
1178 process_properties_changed (NMObject *self, GHashTable *properties, gboolean synchronously)
1179 {
1180         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
1181         GHashTableIter iter;
1182         gpointer name, value;
1183
1184         if (priv->suppress_property_updates)
1185                 return;
1186
1187         g_hash_table_iter_init (&iter, properties);
1188         while (g_hash_table_iter_next (&iter, &name, &value)) {
1189                 if (value)
1190                         handle_property_changed (self, name, value, synchronously);
1191                 else {
1192                         dbgmsg ("%s:%d %s(): object %s property '%s' value is unexpectedly NULL",
1193                                 __FILE__, __LINE__, __func__, G_OBJECT_TYPE_NAME (self), (const char *) name);
1194                 }
1195         }
1196 }
1197
1198 static void
1199 properties_changed_proxy (DBusGProxy *proxy,
1200                           GHashTable *properties,
1201                           gpointer user_data)
1202 {
1203         process_properties_changed (NM_OBJECT (user_data), properties, FALSE);
1204 }
1205
1206 #define HANDLE_TYPE(ucase, lcase, getter) \
1207         } else if (pspec->value_type == G_TYPE_##ucase) { \
1208                 if (G_VALUE_HOLDS_##ucase (value)) { \
1209                         g##lcase *param = (g##lcase *) field; \
1210                         *param = g_value_get_##getter (value); \
1211                 } else { \
1212                         success = FALSE; \
1213                         goto done; \
1214                 }
1215
1216 static gboolean
1217 demarshal_generic (NMObject *object,
1218                    GParamSpec *pspec,
1219                    GValue *value,
1220                    gpointer field)
1221 {
1222         gboolean success = TRUE;
1223
1224         if (pspec->value_type == G_TYPE_STRING) {
1225                 if (G_VALUE_HOLDS_STRING (value)) {
1226                         char **param = (char **) field;
1227                         g_free (*param);
1228                         *param = g_value_dup_string (value);
1229                 } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) {
1230                         char **param = (char **) field;
1231                         g_free (*param);
1232                         *param = g_strdup (g_value_get_boxed (value));
1233                         /* Handle "NULL" object paths */
1234                         if (g_strcmp0 (*param, "/") == 0) {
1235                                 g_free (*param);
1236                                 *param = NULL;
1237                         }
1238                 } else {
1239                         success = FALSE;
1240                         goto done;
1241                 }
1242         HANDLE_TYPE(BOOLEAN, boolean, boolean)
1243         HANDLE_TYPE(CHAR, char, schar)
1244         HANDLE_TYPE(UCHAR, uchar, uchar)
1245         HANDLE_TYPE(DOUBLE, double, double)
1246         HANDLE_TYPE(INT, int, int)
1247         HANDLE_TYPE(UINT, uint, uint)
1248         HANDLE_TYPE(INT64, int64, int64)
1249         HANDLE_TYPE(UINT64, uint64, uint64)
1250         HANDLE_TYPE(LONG, long, long)
1251         HANDLE_TYPE(ULONG, ulong, ulong)
1252         } else {
1253                 dbgmsg ("%s: %s/%s unhandled type %s.",
1254                         __func__,
1255                         G_OBJECT_TYPE_NAME (object),
1256                         pspec->name,
1257                         g_type_name (pspec->value_type));
1258                 success = FALSE;
1259         }
1260
1261 done:
1262         if (success) {
1263                 _nm_object_queue_notify (object, pspec->name);
1264         } else {
1265                 dbgmsg ("%s: %s/%s (type %s) couldn't be set with type %s.",
1266                         __func__, G_OBJECT_TYPE_NAME (object), pspec->name,
1267                         g_type_name (pspec->value_type), G_VALUE_TYPE_NAME (value));
1268         }
1269         return success;
1270 }
1271
1272 void
1273 _nm_object_register_properties (NMObject *object,
1274                                 DBusGProxy *proxy,
1275                                 const NMPropertiesInfo *info)
1276 {
1277         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1278         static gsize dval = 0;
1279         const char *debugstr;
1280         NMPropertiesInfo *tmp;
1281         GHashTable *instance;
1282
1283         g_return_if_fail (NM_IS_OBJECT (object));
1284         g_return_if_fail (proxy != NULL);
1285         g_return_if_fail (info != NULL);
1286
1287         if (g_once_init_enter (&dval)) {
1288                 debugstr = getenv ("LIBNM_GLIB_DEBUG");
1289                 if (debugstr && strstr (debugstr, "properties-changed"))
1290                         debug = TRUE;
1291                 g_once_init_leave (&dval, 1);
1292         }
1293
1294         priv->property_interfaces = g_slist_prepend (priv->property_interfaces,
1295                                                      g_strdup (dbus_g_proxy_get_interface (proxy)));
1296
1297         dbus_g_proxy_add_signal (proxy, "PropertiesChanged", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
1298         dbus_g_proxy_connect_signal (proxy,
1299                                      "PropertiesChanged",
1300                                      G_CALLBACK (properties_changed_proxy),
1301                                      object,
1302                                      NULL);
1303
1304         instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1305         priv->property_tables = g_slist_prepend (priv->property_tables, instance);
1306
1307         for (tmp = (NMPropertiesInfo *) info; tmp->name; tmp++) {
1308                 PropertyInfo *pi;
1309
1310                 if (!tmp->name || (tmp->func && !tmp->field)) {
1311                         g_warning ("%s: missing field in NMPropertiesInfo", __func__);
1312                         continue;
1313                 }
1314
1315                 pi = g_malloc0 (sizeof (PropertyInfo));
1316                 pi->func = tmp->func ? tmp->func : demarshal_generic;
1317                 pi->object_type = tmp->object_type;
1318                 pi->field = tmp->field;
1319                 pi->signal_prefix = tmp->signal_prefix;
1320                 g_hash_table_insert (instance, g_strdup (tmp->name), pi);
1321         }
1322 }
1323
1324 gboolean
1325 _nm_object_reload_properties (NMObject *object, GError **error)
1326 {
1327         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1328         GHashTable *props = NULL;
1329         GSList *p;
1330
1331         if (!priv->property_interfaces || !priv->nm_running)
1332                 return TRUE;
1333
1334         priv->reload_remaining++;
1335
1336         for (p = priv->property_interfaces; p; p = p->next) {
1337                 if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error,
1338                                         G_TYPE_STRING, p->data,
1339                                         G_TYPE_INVALID,
1340                                         DBUS_TYPE_G_MAP_OF_VARIANT, &props,
1341                                         G_TYPE_INVALID))
1342                         return FALSE;
1343
1344                 process_properties_changed (object, props, TRUE);
1345                 g_hash_table_destroy (props);
1346         }
1347
1348         if (--priv->reload_remaining == 0)
1349                 reload_complete (object, TRUE);
1350
1351         return TRUE;
1352 }
1353
1354 void
1355 _nm_object_suppress_property_updates (NMObject *object, gboolean suppress)
1356 {
1357         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1358
1359         priv->suppress_property_updates = suppress;
1360 }
1361
1362
1363 void
1364 _nm_object_ensure_inited (NMObject *object)
1365 {
1366         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1367         GError *error = NULL;
1368
1369         if (!priv->inited) {
1370                 if (!g_initable_init (G_INITABLE (object), NULL, &error)) {
1371                         dbgmsg ("Could not initialize %s %s: %s",
1372                                 G_OBJECT_TYPE_NAME (object),
1373                                 priv->path,
1374                                 error->message);
1375                         g_error_free (error);
1376
1377                         /* Only warn once */
1378                         priv->inited = TRUE;
1379                 }
1380         }
1381 }
1382
1383 void
1384 _nm_object_reload_property (NMObject *object,
1385                             const char *interface,
1386                             const char *prop_name)
1387 {
1388         GValue value = G_VALUE_INIT;
1389         GError *err = NULL;
1390
1391         g_return_if_fail (NM_IS_OBJECT (object));
1392         g_return_if_fail (interface != NULL);
1393         g_return_if_fail (prop_name != NULL);
1394
1395         if (!NM_OBJECT_GET_PRIVATE (object)->nm_running)
1396                 return;
1397
1398         if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
1399                                              "Get", 15000, &err,
1400                                              G_TYPE_STRING, interface,
1401                                              G_TYPE_STRING, prop_name,
1402                                              G_TYPE_INVALID,
1403                                              G_TYPE_VALUE, &value,
1404                                              G_TYPE_INVALID)) {
1405                 dbgmsg ("%s: Error getting '%s' for %s: %s\n",
1406                         __func__,
1407                         prop_name,
1408                         nm_object_get_path (object),
1409                         err->message);
1410                 g_clear_error (&err);
1411                 return;
1412         }
1413
1414         handle_property_changed (object, prop_name, &value, TRUE);
1415         g_value_unset (&value);
1416 }
1417
1418 void
1419 _nm_object_set_property (NMObject *object,
1420                          const char *interface,
1421                          const char *prop_name,
1422                          GValue *value)
1423 {
1424         g_return_if_fail (NM_IS_OBJECT (object));
1425         g_return_if_fail (interface != NULL);
1426         g_return_if_fail (prop_name != NULL);
1427         g_return_if_fail (G_IS_VALUE (value));
1428
1429         if (!NM_OBJECT_GET_PRIVATE (object)->nm_running)
1430                 return;
1431
1432         if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
1433                                              "Set", 2000, NULL,
1434                                              G_TYPE_STRING, interface,
1435                                              G_TYPE_STRING, prop_name,
1436                                              G_TYPE_VALUE, value,
1437                                              G_TYPE_INVALID)) {
1438
1439                 /* Ignore errors. dbus_g_proxy_call_with_timeout() is called instead of
1440                  * dbus_g_proxy_call_no_reply() to give NM chance to authenticate the caller.
1441                  */
1442         }
1443 }
1444
1445 static void
1446 reload_complete (NMObject *object, gboolean emit_now)
1447 {
1448         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1449         GSimpleAsyncResult *simple;
1450         GSList *results, *iter;
1451         GError *error;
1452
1453         if (emit_now) {
1454                 if (priv->notify_id) {
1455                         g_source_remove (priv->notify_id);
1456                         priv->notify_id = 0;
1457                 }
1458                 deferred_notify_cb (object);
1459         } else
1460                 _nm_object_defer_notify (object);
1461
1462         results = priv->reload_results;
1463         priv->reload_results = NULL;
1464         error = priv->reload_error;
1465         priv->reload_error = NULL;
1466
1467         for (iter = results; iter; iter = iter->next) {
1468                 simple = iter->data;
1469
1470                 if (error)
1471                         g_simple_async_result_set_from_error (simple, error);
1472                 else
1473                         g_simple_async_result_set_op_res_gboolean (simple, TRUE);
1474
1475                 g_simple_async_result_complete (simple);
1476                 g_object_unref (simple);
1477         }
1478         g_slist_free (results);
1479         g_clear_error (&error);
1480 }
1481
1482 static void
1483 reload_got_properties (DBusGProxy *proxy, DBusGProxyCall *call,
1484                        gpointer user_data)
1485 {
1486         NMObject *object = user_data;
1487         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1488         GHashTable *props = NULL;
1489         GError *error = NULL;
1490
1491         if (dbus_g_proxy_end_call (proxy, call, &error,
1492                                    DBUS_TYPE_G_MAP_OF_VARIANT, &props,
1493                                    G_TYPE_INVALID)) {
1494                 process_properties_changed (object, props, FALSE);
1495                 g_hash_table_destroy (props);
1496         } else {
1497                 if (priv->reload_error)
1498                         g_error_free (error);
1499                 else
1500                         priv->reload_error = error;
1501         }
1502
1503         if (--priv->reload_remaining == 0)
1504                 reload_complete (object, FALSE);
1505 }
1506
1507 void
1508 _nm_object_reload_properties_async (NMObject *object, GAsyncReadyCallback callback, gpointer user_data)
1509 {
1510         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
1511         GSimpleAsyncResult *simple;
1512         GSList *p;
1513
1514         simple = g_simple_async_result_new (G_OBJECT (object), callback,
1515                                             user_data, _nm_object_reload_properties_async);
1516
1517         if (!priv->property_interfaces) {
1518                 g_simple_async_result_complete_in_idle (simple);
1519                 g_object_unref (simple);
1520                 return;
1521         }
1522
1523         priv->reload_results = g_slist_prepend (priv->reload_results, simple);
1524
1525         /* If there was already a reload happening, we don't need to
1526          * re-read the properties again, we just need to wait for the
1527          * existing reload to finish.
1528          */
1529         if (priv->reload_results->next)
1530                 return;
1531
1532         for (p = priv->property_interfaces; p; p = p->next) {
1533                 priv->reload_remaining++;
1534                 dbus_g_proxy_begin_call (priv->properties_proxy, "GetAll",
1535                                          reload_got_properties, object, NULL,
1536                                          G_TYPE_STRING, p->data,
1537                                          G_TYPE_INVALID);
1538         }
1539 }
1540
1541 gboolean
1542 _nm_object_reload_properties_finish (NMObject *object, GAsyncResult *result, GError **error)
1543 {
1544         GSimpleAsyncResult *simple;
1545
1546         g_return_val_if_fail (NM_IS_OBJECT (object), FALSE);
1547         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (object), _nm_object_reload_properties_async), FALSE);
1548
1549         simple = G_SIMPLE_ASYNC_RESULT (result);
1550         if (g_simple_async_result_propagate_error (simple, error))
1551                 return FALSE;
1552
1553         return g_simple_async_result_get_op_res_gboolean (simple);
1554 }
1555
1556 DBusGProxy *
1557 _nm_object_new_proxy (NMObject *self, const char *path, const char *interface)
1558 {
1559         NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
1560
1561         return _nm_dbus_new_proxy_for_connection (priv->connection, path ? path : priv->path, interface);
1562 }