diff options
Diffstat (limited to 'libnm/nm-client.c')
| -rw-r--r-- | libnm/nm-client.c | 7612 |
1 files changed, 5776 insertions, 1836 deletions
diff --git a/libnm/nm-client.c b/libnm/nm-client.c index c39f6267..1a1ed256 100644 --- a/libnm/nm-client.c +++ b/libnm/nm-client.c @@ -1,21 +1,7 @@ +// SPDX-License-Identifier: LGPL-2.1+ /* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Copyright 2007 - 2008 Novell, Inc. - * Copyright 2007 - 2018 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -24,33 +10,16 @@ #include <libudev.h> -#include "nm-utils.h" -#include "nm-manager.h" -#include "nm-dns-manager.h" -#include "nm-remote-settings.h" -#include "nm-device-ethernet.h" -#include "nm-device-wifi.h" -#include "nm-core-internal.h" -#include "nm-active-connection.h" -#include "nm-vpn-connection.h" -#include "nm-remote-connection.h" -#include "nm-dbus-helpers.h" -#include "nm-wimax-nsp.h" -#include "nm-object-private.h" - -#include "introspection/org.freedesktop.NetworkManager.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Wireless.h" -#include "introspection/org.freedesktop.NetworkManager.Device.WifiP2P.h" -#include "introspection/org.freedesktop.NetworkManager.Device.h" -#include "introspection/org.freedesktop.NetworkManager.DnsManager.h" -#include "introspection/org.freedesktop.NetworkManager.Settings.h" -#include "introspection/org.freedesktop.NetworkManager.Settings.Connection.h" -#include "introspection/org.freedesktop.NetworkManager.VPN.Connection.h" -#include "introspection/org.freedesktop.NetworkManager.Connection.Active.h" +#include "nm-std-aux/c-list-util.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-dbus-aux.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-access-point.h" #include "nm-active-connection.h" #include "nm-checkpoint.h" +#include "nm-core-internal.h" +#include "nm-dbus-helpers.h" #include "nm-device-6lowpan.h" #include "nm-device-adsl.h" #include "nm-device-bond.h" @@ -75,51 +44,156 @@ #include "nm-device-vxlan.h" #include "nm-device-wifi-p2p.h" #include "nm-device-wifi.h" -#include "nm-device-wimax.h" #include "nm-device-wireguard.h" #include "nm-device-wpan.h" #include "nm-dhcp-config.h" #include "nm-dhcp4-config.h" #include "nm-dhcp6-config.h" +#include "nm-dns-manager.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-manager.h" -#include "nm-wifi-p2p-peer.h" +#include "nm-object-private.h" #include "nm-remote-connection.h" -#include "nm-remote-settings.h" +#include "nm-utils.h" #include "nm-vpn-connection.h" +#include "nm-wifi-p2p-peer.h" -void _nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device, gboolean enabled); +/*****************************************************************************/ -static void nm_client_initable_iface_init (GInitableIface *iface); -static void nm_client_async_initable_iface_init (GAsyncInitableIface *iface); +static NM_CACHED_QUARK_FCN ("nm-client-context-busy-watcher", nm_client_context_busy_watcher_quark) -G_DEFINE_TYPE_WITH_CODE (NMClient, nm_client, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_client_initable_iface_init); - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_client_async_initable_iface_init); - ) +static void +_context_busy_watcher_attach_integration_source_cb (gpointer data, + GObject *where_the_object_was) +{ + nm_g_source_destroy_and_unref (data); +} + +static void +_context_busy_watcher_attach_integration_source (GObject *context_busy_watcher, + GSource *source_take) +{ + /* The problem is... + * + * NMClient is associated with a GMainContext, just like its underlying GDBusConnection + * also queues signals and callbacks on that main context. During operation, NMClient + * will schedule async operations which will return asynchronously on the GMainContext. + * + * Note that depending on whether NMClient got initialized synchronously or asynchronously, + * it has an internal priv->dbus_context that is different from the outer priv->main_context. + * However, the problem is in both cases. + * + * So, as long as there are pending D-Bus calls, the GMainContext is referenced and kept alive. + * When NMClient gets destroyed, the pending calls get cancelled, but the async callback are still + * scheduled to return. + * That means, the main context stays alive until it gets iterated long enough so that all pending + * operations are completed. + * + * Note that pending operations don't keep NMClient alive, so NMClient can already be gone by + * then, but the user still should iterate the main context long enough to process the (cancelled) + * callbacks... at least, if the user cares about whether the remaining memory and file descriptors + * of the GMainContext can be reclaimed. + * + * In hindsight, maybe pending references should kept NMClient alive. But then NMClient would + * need a special "shutdown()" API that the user must invoke, because unrefing would no longer + * be enough to ensure a shutdown (imagine a situation where NMClient receives a constant flow + * of "CheckPermissions" signals, which keeps retriggering an async request). Anyway, we cannot + * add such a shutdown API now, as it would break client's expectations that they can just unref + * the NMClient to destroy it. + * + * So, we allow NMClient to unref, but the user is advised to keep iterating the main context. + * But for how long? Here comes nm_client_get_context_busy_watcher() into play. The user may + * subscribe a weak pointer to that instance and should keep iterating as long as the object + * exists. + * + * Now, back to synchronous initialization. Here we have the internal priv->dbus_context context. + * We also cannot remove that context right away, instead we need to keep it integrated in the + * caller's priv->main_context as long as we have pending calls: that is, as long as the + * context-busy-watcher is alive. + */ + + g_object_weak_ref (context_busy_watcher, + _context_busy_watcher_attach_integration_source_cb, + source_take); +} -#define NM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CLIENT, NMClientPrivate)) +/*****************************************************************************/ typedef struct { - NMClient *client; - GCancellable *cancellable; - GSimpleAsyncResult *result; - int pending_init; -} NMClientInitData; + + /* It is quite wasteful to require 2 pointers per property (of an instance) only to track whether + * the property got changed. But it's convenient! */ + CList changed_prop_lst; + + GVariant *prop_data_value; +} NMLDBusObjPropData; typedef struct { - NMManager *manager; - NMRemoteSettings *settings; - NMDnsManager *dns_manager; - GDBusObjectManager *object_manager; - GCancellable *new_object_manager_cancellable; - struct udev *udev; - bool udev_inited:1; -} NMClientPrivate; + CList iface_lst; + union { + const NMLDBusMetaIface *meta; + NMRefString *name; + } dbus_iface; + + CList changed_prop_lst_head; + + /* We also keep track of non-well known interfaces. The presence of a D-Bus interface + * is what makes a D-Bus alive or not. As we should track all D-Bus objects, we also + * need to track whether there are any interfaces on it -- even if we otherwise don't + * care about the interface. */ + bool dbus_iface_is_wellknown:1; + + /* if TRUE, the interface is about to be removed. */ + bool iface_removed:1; + + bool nmobj_checked:1; + bool nmobj_compatible:1; + + NMLDBusObjPropData prop_datas[]; +} NMLDBusObjIfaceData; + +/* The dbus_path must be the first element, so when we hash the object by the dbus_path, + * we also can lookup the object by only having a NMRefString at hand + * using nm_pdirect_hash()/nm_pdirect_equal(). */ +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMLDBusObject, dbus_path) == 0); + +typedef void (*NMLDBusObjWatchNotifyFcn) (NMClient *client, + gpointer obj_watcher); + +struct _NMLDBusObjWatcher { + NMLDBusObject *dbobj; + struct { + CList watcher_lst; + NMLDBusObjWatchNotifyFcn notify_fcn; + } _priv; +}; -enum { - PROP_0, +typedef struct { + NMLDBusObjWatcher parent; + gpointer user_data; +} NMLDBusObjWatcherWithPtr; + +/*****************************************************************************/ + +typedef struct { + GCancellable *cancellable; + GSource *cancel_on_idle_source; + gulong cancelled_id; + union { + struct { + GTask *task; + } async; + struct { + GMainLoop *main_loop; + GError **error_location; + } sync; + } data; + bool is_sync:1; +} InitData; + +NM_GOBJECT_PROPERTIES_DEFINE (NMClient, + PROP_DBUS_CONNECTION, + PROP_DBUS_NAME_OWNER, PROP_VERSION, PROP_STATE, PROP_STARTUP, @@ -133,6 +207,7 @@ enum { PROP_WIMAX_HARDWARE_ENABLED, PROP_ACTIVE_CONNECTIONS, PROP_CONNECTIVITY, + PROP_CONNECTIVITY_CHECK_URI, PROP_CONNECTIVITY_CHECK_AVAILABLE, PROP_CONNECTIVITY_CHECK_ENABLED, PROP_PRIMARY_CONNECTION, @@ -147,9 +222,7 @@ enum { PROP_DNS_RC_MANAGER, PROP_DNS_CONFIGURATION, PROP_CHECKPOINTS, - - LAST_PROP -}; +); enum { DEVICE_ADDED, @@ -167,7 +240,150 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static const GPtrArray empty = { 0, }; +enum { + PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION = 0, + PROPERTY_O_IDX_NM_PRIMAY_CONNECTION, + _PROPERTY_O_IDX_NM_NUM, +}; + +enum { + PROPERTY_AO_IDX_DEVICES = 0, + PROPERTY_AO_IDX_ALL_DEVICES, + PROPERTY_AO_IDX_ACTIVE_CONNECTIONS, + PROPERTY_AO_IDX_CHECKPOINTS, + _PROPERTY_AO_IDX_NM_NUM, +}; + +typedef struct { + struct udev *udev; + GMainContext *main_context; + GMainContext *dbus_context; + GObject *context_busy_watcher; + GDBusConnection *dbus_connection; + InitData *init_data; + GHashTable *dbus_objects; + CList obj_changed_lst_head; + GCancellable *name_owner_get_cancellable; + GCancellable *get_managed_objects_cancellable; + + CList queue_notify_lst_head; + CList notify_event_lst_head; + + CList dbus_objects_lst_head_watched_only; + CList dbus_objects_lst_head_on_dbus; + CList dbus_objects_lst_head_with_nmobj_not_ready; + CList dbus_objects_lst_head_with_nmobj_ready; + + NMLDBusObject *dbobj_nm; + NMLDBusObject *dbobj_settings; + NMLDBusObject *dbobj_dns_manager; + + GHashTable *permissions; + GCancellable *permissions_cancellable; + + char *name_owner; + guint name_owner_changed_id; + guint dbsid_nm_object_manager; + guint dbsid_dbus_properties_properties_changed; + guint dbsid_nm_settings_connection_updated; + guint dbsid_nm_connection_active_state_changed; + guint dbsid_nm_vpn_connection_state_changed; + guint dbsid_nm_check_permissions; + + bool udev_inited:1; + bool notify_event_lst_changed:1; + bool check_dbobj_visible_all:1; + bool nm_running:1; + + struct { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NM_NUM]; + NMLDBusPropertyAO property_ao[_PROPERTY_AO_IDX_NM_NUM]; + char *connectivity_check_uri; + char *version; + guint32 connectivity; + guint32 state; + guint32 metered; + bool connectivity_check_available; + bool connectivity_check_enabled; + bool networking_enabled; + bool startup; + bool wireless_enabled; + bool wireless_hardware_enabled; + bool wwan_enabled; + bool wwan_hardware_enabled; + } nm; + + struct { + NMLDBusPropertyAO connections; + char *hostname; + bool can_modify; + } settings; + + struct { + GPtrArray *configuration; + char *mode; + char *rc_manager; + } dns_manager; + +} NMClientPrivate; + +struct _NMClient { + union { + GObject parent; + NMObjectBase obj_base; + }; + NMClientPrivate _priv; +}; + +struct _NMClientClass { + union { + GObjectClass parent; + NMObjectBaseClass obj_base; + }; +}; + +static void nm_client_initable_iface_init (GInitableIface *iface); +static void nm_client_async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NMClient, nm_client, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_client_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_client_async_initable_iface_init); + ) + +#define NM_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMClient, NM_IS_CLIENT) + +/*****************************************************************************/ + +static void _init_start_check_complete (NMClient *self); + +static void name_owner_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data); + +static void name_owner_get_call (NMClient *self); + +static void _set_nm_running (NMClient *self); + +/*****************************************************************************/ + +static NMRefString *_dbus_path_nm = NULL; +static NMRefString *_dbus_path_settings = NULL; +static NMRefString *_dbus_path_dns_manager = NULL; + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nml_dbus_obj_state_to_string, NMLDBusObjState, + NM_UTILS_LOOKUP_DEFAULT_WARN ("???"), + NM_UTILS_LOOKUP_ITEM (NML_DBUS_OBJ_STATE_UNLINKED, "unlinked"), + NM_UTILS_LOOKUP_ITEM (NML_DBUS_OBJ_STATE_WATCHED_ONLY, "watched-only"), + NM_UTILS_LOOKUP_ITEM (NML_DBUS_OBJ_STATE_ON_DBUS, "on-dbus"), + NM_UTILS_LOOKUP_ITEM (NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, "not-ready"), + NM_UTILS_LOOKUP_ITEM (NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, "ready"), +); /*****************************************************************************/ @@ -182,23 +398,3390 @@ NM_CACHED_QUARK_FCN ("nm-client-error-quark", nm_client_error_quark) /*****************************************************************************/ +static InitData * +_init_data_new_sync (GCancellable *cancellable, + GMainLoop *main_loop, + GError **error_location) +{ + InitData *init_data; + + init_data = g_slice_new (InitData); + *init_data = (InitData) { + .cancellable = nm_g_object_ref (cancellable), + .is_sync = TRUE, + .data.sync = { + .main_loop = main_loop, + .error_location = error_location, + }, + }; + return init_data; +} + +static InitData * +_init_data_new_async (GCancellable *cancellable, + GTask *task_take) +{ + InitData *init_data; + + init_data = g_slice_new (InitData); + *init_data = (InitData) { + .cancellable = nm_g_object_ref (cancellable), + .is_sync = FALSE, + .data.async = { + .task = g_steal_pointer (&task_take), + }, + }; + return init_data; +} + +/*****************************************************************************/ + +GError * +_nm_client_new_error_nm_not_running (void) +{ + return g_error_new_literal (NM_CLIENT_ERROR, + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, + "NetworkManager is not running"); +} + +GError * +_nm_client_new_error_nm_not_cached (void) +{ + return g_error_new_literal (NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + "Object is no longer in the client cache"); +} + +static void +_nm_client_dbus_call_simple_cb (GObject *source, GAsyncResult *result, gpointer data) +{ + GAsyncReadyCallback callback; + gpointer user_data; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack (data, &callback, &user_data, &context_busy_watcher); + + callback (source, result, user_data); +} + +void +_nm_client_dbus_call_simple (NMClient *self, + GCancellable *cancellable, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + nm_assert (priv->name_owner); + nm_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); + nm_assert (object_path); + nm_assert (interface_name); + nm_assert (method_name); + nm_assert (parameters); + nm_assert (reply_type); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->dbus_context); + + g_dbus_connection_call (priv->dbus_connection, + priv->name_owner, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + _nm_client_dbus_call_simple_cb, + nm_utils_user_data_pack (callback, user_data, g_object_ref (priv->context_busy_watcher))); +} + +void +_nm_client_dbus_call (NMClient *self, + gpointer source_obj, + gpointer source_tag, + GCancellable *cancellable, + GAsyncReadyCallback user_callback, + gpointer user_callback_data, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback internal_callback) +{ + NMClientPrivate *priv; + gs_unref_object GTask *task = NULL; + + nm_assert (!source_obj || G_IS_OBJECT (source_obj)); + nm_assert (source_tag); + nm_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + nm_assert (internal_callback); + nm_assert (object_path); + nm_assert (interface_name); + nm_assert (method_name); + nm_assert (parameters); + nm_assert (reply_type); + + task = nm_g_task_new (source_obj, cancellable, source_tag, user_callback, user_callback_data); + + if (!self) { + nm_g_variant_unref_floating (parameters); + g_task_return_error (task, _nm_client_new_error_nm_not_cached ()); + return; + } + + priv = NM_CLIENT_GET_PRIVATE (self); + if (!priv->name_owner) { + nm_g_variant_unref_floating (parameters); + g_task_return_error (task, _nm_client_new_error_nm_not_running ()); + return; + } + + _nm_client_dbus_call_simple (self, + cancellable, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + internal_callback, + g_steal_pointer (&task)); +} + +GVariant * +_nm_client_dbus_call_sync (NMClient *self, + GCancellable *cancellable, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError **error) +{ + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + + nm_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + nm_assert (!error || !*error); + nm_assert (object_path); + nm_assert (interface_name); + nm_assert (method_name); + nm_assert (parameters); + nm_assert (reply_type); + + if (!self) { + nm_g_variant_unref_floating (parameters); + nm_g_set_error_take_lazy (error, _nm_client_new_error_nm_not_cached ()); + return NULL; + } + + priv = NM_CLIENT_GET_PRIVATE (self); + if (!priv->name_owner) { + nm_g_variant_unref_floating (parameters); + nm_g_set_error_take_lazy (error, _nm_client_new_error_nm_not_running ()); + return NULL; + } + + ret = g_dbus_connection_call_sync (priv->dbus_connection, + priv->name_owner, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + error); + if (!ret) { + if (error && strip_dbus_error) + g_dbus_error_strip_remote_error (*error); + return NULL; + } + + return g_steal_pointer (&ret); +} + +gboolean +_nm_client_dbus_call_sync_void (NMClient *self, + GCancellable *cancellable, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + ret = _nm_client_dbus_call_sync (self, + cancellable, + object_path, + interface_name, + method_name, + parameters, + G_VARIANT_TYPE ("()"), + flags, + timeout_msec, + strip_dbus_error, + error); + return !!ret; +} + +void +_nm_client_set_property_sync_legacy (NMClient *self, + const char *object_path, + const char *interface_name, + const char *property_name, + const char *format_string, + ...) +{ + NMClientPrivate *priv; + GVariant *val; + gs_unref_variant GVariant *ret = NULL; + va_list ap; + + nm_assert (!self || NM_IS_CLIENT (self)); + nm_assert (interface_name); + nm_assert (property_name); + nm_assert (format_string); + + if (!self) + return; + + priv = NM_CLIENT_GET_PRIVATE (self); + if (!priv->name_owner) + return; + + va_start (ap, format_string); + val = g_variant_new_va (format_string, NULL, &ap); + va_end (ap); + + nm_assert (val); + + /* A synchronous D-Bus call that is not cancellable an ignores the return value. + * This function only exists for backward compatibility. */ + ret = g_dbus_connection_call_sync (priv->dbus_connection, + priv->name_owner, + object_path, + DBUS_INTERFACE_PROPERTIES, + "Set", + g_variant_new ("(ssv)", + interface_name, + property_name, + val), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 2000, + NULL, + NULL); +} + +/*****************************************************************************/ + +#define _assert_main_context_is_current_source(self, x_context) \ + G_STMT_START { \ + if (NM_MORE_ASSERTS > 0) { \ + GSource *_source = g_main_current_source (); \ + \ + if (_source) { \ + NMClientPrivate *_priv = NM_CLIENT_GET_PRIVATE (self); \ + \ + nm_assert (g_source_get_context (_source) == _priv->x_context); \ + nm_assert (g_main_context_is_owner (_priv->x_context)); \ + } \ + } \ + } G_STMT_END + +#define _assert_main_context_is_current_thread_default(self, x_context) \ + G_STMT_START { \ + if (NM_MORE_ASSERTS > 0) { \ + NMClientPrivate *_priv = NM_CLIENT_GET_PRIVATE (self); \ + \ + nm_assert ((g_main_context_get_thread_default () ?: g_main_context_default ()) == _priv->x_context); \ + nm_assert (g_main_context_is_owner (_priv->x_context)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +void +_nm_client_queue_notify_object (NMClient *self, + gpointer nmobj, + const GParamSpec *pspec) +{ + NMObjectBase *base; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NM_IS_OBJECT (nmobj) || NM_IS_CLIENT (nmobj)); + + base = (NMObjectBase *) nmobj; + + if (base->is_disposing) { + /* Don't emit property changed signals once the NMClient + * instance is about to shut down. */ + nm_assert (nmobj == self); + return; + } + + if (c_list_is_empty (&base->queue_notify_lst)) { + c_list_link_tail (&NM_CLIENT_GET_PRIVATE (self)->queue_notify_lst_head, + &base->queue_notify_lst); + g_object_ref (nmobj); + g_object_freeze_notify (nmobj); + } + + if (pspec) + g_object_notify_by_pspec (nmobj, (GParamSpec *) pspec); +} + +/*****************************************************************************/ + +gpointer +_nm_client_notify_event_queue (NMClient *self, + int priority, + NMClientNotifyEventCb callback, + gsize event_size) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMClientNotifyEvent *notify_event; + + nm_assert (callback); + nm_assert (event_size > sizeof (NMClientNotifyEvent)); + + notify_event = g_malloc (event_size); + notify_event->priority = priority; + notify_event->callback = callback; + c_list_link_tail (&priv->notify_event_lst_head, ¬ify_event->lst); + priv->notify_event_lst_changed = TRUE; + return notify_event; +} + +NMClientNotifyEventWithPtr * +_nm_client_notify_event_queue_with_ptr (NMClient *self, + int priority, + NMClientNotifyEventWithPtrCb callback, + gpointer user_data) +{ + NMClientNotifyEventWithPtr *notify_event; + + notify_event = _nm_client_notify_event_queue (self, + priority, + (NMClientNotifyEventCb) callback, + sizeof (NMClientNotifyEventWithPtr)); + notify_event->user_data = user_data; + return notify_event; +} + +/*****************************************************************************/ + +typedef struct { + NMClientNotifyEvent parent; + GObject *source; + NMObject *obj; + guint signal_id; +} NMClientNotifyEventObjAddedRemove; + +static void +_nm_client_notify_event_queue_emit_obj_signal_cb (NMClient *self, + gpointer notify_event_base) +{ + NMClientNotifyEventObjAddedRemove *notify_event = notify_event_base; + + NML_NMCLIENT_LOG_T (self, "[%s] emit \"%s\" signal for %s", + NM_IS_CLIENT (notify_event->source) + ? "nmclient" + : _nm_object_get_path (notify_event->source), + g_signal_name (notify_event->signal_id), + _nm_object_get_path (notify_event->obj)); + + nm_assert ( NM_IS_OBJECT (notify_event->source) + || NM_IS_CLIENT (notify_event->source)); + + g_signal_emit (notify_event->source, + notify_event->signal_id, + 0, + notify_event->obj); + + g_object_unref (notify_event->obj); + g_object_unref (notify_event->source); +} + +void +_nm_client_notify_event_queue_emit_obj_signal (NMClient *self, + GObject *source, + NMObject *nmobj, + gboolean is_added /* or else removed */, + int prio_offset, + guint signal_id) +{ + NMClientNotifyEventObjAddedRemove *notify_event; + + nm_assert (prio_offset >= 0); + nm_assert (prio_offset < 20); + nm_assert ( NM_IS_OBJECT (source) + || NM_IS_CLIENT (source)); + nm_assert (NM_IS_OBJECT (nmobj)); + + if (((NMObjectBase *) source)->is_disposing) { + nm_assert (NM_IS_CLIENT (source)); + return; + } + + notify_event = _nm_client_notify_event_queue (self, + is_added + ? NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER - 20 + prio_offset + : NM_CLIENT_NOTIFY_EVENT_PRIO_BEFORE + 20 - prio_offset, + _nm_client_notify_event_queue_emit_obj_signal_cb, + sizeof (NMClientNotifyEventObjAddedRemove)); + notify_event->source = g_object_ref (source); + notify_event->obj = g_object_ref (nmobj); + notify_event->signal_id = signal_id; +} + +/*****************************************************************************/ + +static int +_nm_client_notify_event_cmp (const CList *a, + const CList *b, + const void *user_data) +{ + NM_CMP_DIRECT (c_list_entry (a, NMClientNotifyEvent, lst)->priority, + c_list_entry (b, NMClientNotifyEvent, lst)->priority); + return 0; +} + +static void +_nm_client_notify_event_emit_parts (NMClient *self, + int max_priority /* included! */) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMClientNotifyEvent *notify_event; + + while (TRUE) { + if (priv->notify_event_lst_changed) { + priv->notify_event_lst_changed = FALSE; + c_list_sort (&priv->notify_event_lst_head, _nm_client_notify_event_cmp, NULL); + } + notify_event = c_list_first_entry (&priv->notify_event_lst_head, NMClientNotifyEvent, lst); + if (!notify_event) + return; + if (notify_event->priority > max_priority) + return; + c_list_unlink_stale (¬ify_event->lst); + notify_event->callback (self, notify_event); + g_free (notify_event); + } +} + +static void +_nm_client_notify_event_emit (NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMObjectBase *base; + + _nm_client_notify_event_emit_parts (self, NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP); + + while ((base = c_list_first_entry (&priv->queue_notify_lst_head, NMObjectBase, queue_notify_lst))) { + c_list_unlink (&base->queue_notify_lst); + g_object_thaw_notify (G_OBJECT (base)); + g_object_unref (base); + } + + _nm_client_notify_event_emit_parts (self, G_MAXINT); +} + +/*****************************************************************************/ + +GDBusConnection * +_nm_client_get_dbus_connection (NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE (self)->dbus_connection; +} + +const char * +_nm_client_get_dbus_name_owner (NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE (self)->name_owner; +} + +GMainContext * +_nm_client_get_context_main (NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE (self)->main_context; +} + +GMainContext * +_nm_client_get_context_dbus (NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE (self)->dbus_context; +} + +/** + * nm_client_get_main_context: + * @self: the #NMClient instance + * + * The #NMClient instance is permanently associated with the current + * thread default #GMainContext, referenced the time when the instance + * was created. To receive events, the user must iterate this context + * and can use it to synchronize access to the client. + * + * Note that even after #NMClient instance got destroyed, there might + * still be pending sources registered in the context. That means, to fully + * clean up, the user must continue iterating the context as long as + * the nm_client_get_context_busy_watcher() object is alive. + * + * Returns: (transfer none): the #GMainContext of the client. + * + * Since: 1.22 + */ +GMainContext * +nm_client_get_main_context (NMClient *self) +{ + g_return_val_if_fail (NM_IS_CLIENT (self), NULL); + + return _nm_client_get_context_main (self); +} + +/** + * nm_client_get_context_busy_watcher: + * @self: the NMClient instance. + * + * Returns: (transfer none): a GObject that stays alive as long as there are pending + * D-Bus operations. + * + * NMClient will schedule asynchronous D-Bus requests which will complete on + * the GMainContext associated with the instance. When destroying the NMClient + * instance, those requests are cancelled right away, however their pending requests are + * still outstanding and queued in the GMainContext. These outstanding callbacks + * keep the GMainContext alive. In order to fully release all resources, + * the user must keep iterating the main context until all these callbacks + * are handled. Of course, at this point no more actual callbacks will be invoked + * for the user, those are all internally cancelled. + * + * This just leaves one problem: how long does the user need to keep the + * GMainContext running to ensure everything is cleaned up? The answer is + * this GObject. Subscribe a weak reference to the returned object and keep + * iterating the main context until the object got unreferenced. + * + * Note that after the NMClient instance gets destroyed, the remaining callbacks + * will be invoked right away. That means, the user won't have to iterate the + * main context much longer. + * + * Since: 1.22 + */ +GObject * +nm_client_get_context_busy_watcher (NMClient *self) +{ + GObject *w; + + g_return_val_if_fail (NM_IS_CLIENT (self), NULL); + + w = NM_CLIENT_GET_PRIVATE (self)->context_busy_watcher; + return g_object_get_qdata (w, nm_client_context_busy_watcher_quark ()) + ?: w; +} + +struct udev * +_nm_client_get_udev (NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + if (G_UNLIKELY (!priv->udev_inited)) { + priv->udev_inited = TRUE; + /* for testing, we don't want to use udev in libnm. */ + if (!nm_streq0 (g_getenv ("LIBNM_USE_NO_UDEV"), "1")) + priv->udev = udev_new (); + } + + return priv->udev; +} + +/*****************************************************************************/ + +static void +_ASSERT_dbobj (NMLDBusObject *dbobj, + NMClient *self) +{ +#if NM_MORE_ASSERTS > 5 + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NML_IS_DBUS_OBJECT (dbobj)); + nm_assert (dbobj == g_hash_table_lookup (NM_CLIENT_GET_PRIVATE (self)->dbus_objects, dbobj)); +#endif +} + +static NMLDBusObject * +nml_dbus_object_new (NMRefString *dbus_path_take) +{ + NMLDBusObject *dbobj; + + nm_assert (NM_IS_REF_STRING (dbus_path_take)); + + dbobj = g_slice_new (NMLDBusObject); + *dbobj = (NMLDBusObject) { + .dbus_path = g_steal_pointer (&dbus_path_take), + .ref_count = 1, + .dbus_objects_lst = C_LIST_INIT (dbobj->dbus_objects_lst), + .iface_lst_head = C_LIST_INIT (dbobj->iface_lst_head), + .watcher_lst_head = C_LIST_INIT (dbobj->watcher_lst_head), + .obj_changed_lst = C_LIST_INIT (dbobj->obj_changed_lst), + .obj_state = NML_DBUS_OBJ_STATE_UNLINKED, + }; + return dbobj; +} + +NMLDBusObject * +nml_dbus_object_ref (NMLDBusObject *dbobj) +{ + nm_assert (dbobj); + nm_assert (dbobj->ref_count > 0); + + dbobj->ref_count++; + return dbobj; +} + +void +nml_dbus_object_unref (NMLDBusObject *dbobj) +{ + nm_assert (dbobj); + nm_assert (dbobj->ref_count > 0); + + if (--dbobj->ref_count > 0) + return; + + nm_assert (c_list_is_empty (&dbobj->obj_changed_lst)); + nm_assert (c_list_is_empty (&dbobj->iface_lst_head)); + nm_assert (c_list_is_empty (&dbobj->watcher_lst_head)); + nm_assert (!dbobj->nmobj); + + nm_ref_string_unref (dbobj->dbus_path); + nm_g_slice_free (dbobj); +} + +static NMLDBusObjIfaceData * +nml_dbus_object_iface_data_get (NMLDBusObject *dbobj, + const char *dbus_iface_name, + gboolean allow_create) +{ + const NMLDBusMetaIface *meta_iface; + NMLDBusObjIfaceData *db_iface_data; + NMLDBusObjPropData *db_prop_data; + guint count = 0; + guint i; + + nm_assert (NML_IS_DBUS_OBJECT (dbobj)); + nm_assert (dbus_iface_name); + +#if NM_MORE_ASSERTS > 10 + { + gboolean expect_well_known = TRUE; + + /* all well-known interfaces must come first in the list. */ + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (db_iface_data->dbus_iface_is_wellknown == expect_well_known) + continue; + nm_assert (expect_well_known); + expect_well_known = FALSE; + } + } +#endif + + meta_iface = nml_dbus_meta_iface_get (dbus_iface_name); + if (meta_iface) { + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->iface_removed) + continue; + if (db_iface_data->dbus_iface.meta == meta_iface) + return db_iface_data; + count++; + } + } else { + nm_c_list_for_each_entry_prev (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->iface_removed) + continue; + if (nm_streq (db_iface_data->dbus_iface.name->str, dbus_iface_name)) + return db_iface_data; + count++; + } + } + + if (!allow_create) + return NULL; + + if (count > 20) { + /* We track the list of interfaces that an object has in a linked list. + * That is efficient and convenient, if we assume that each object only has a small + * number of interfaces (which very much should be the case). Here, something is very + * odd, maybe there is a bug or the server side is misbehaving. Anyway, error out. */ + return NULL; + } + + db_iface_data = g_malloc ( G_STRUCT_OFFSET (NMLDBusObjIfaceData, prop_datas) + + (meta_iface ? (sizeof (NMLDBusObjPropData) * meta_iface->n_dbus_properties): 0u)); + if (meta_iface) { + *db_iface_data = (NMLDBusObjIfaceData) { + .dbus_iface.meta = meta_iface, + .dbus_iface_is_wellknown = TRUE, + .changed_prop_lst_head = C_LIST_INIT (db_iface_data->changed_prop_lst_head), + .iface_removed = FALSE, + }; + db_prop_data = &db_iface_data->prop_datas[0]; + for (i = 0; i < meta_iface->n_dbus_properties; i++, db_prop_data++) { + *db_prop_data = (NMLDBusObjPropData) { + .prop_data_value = NULL, + .changed_prop_lst = C_LIST_INIT (db_prop_data->changed_prop_lst), + }; + } + c_list_link_front (&dbobj->iface_lst_head, &db_iface_data->iface_lst); + } else { + /* Intentionally don't initialize the other fields. We are not supposed + * to touch them, and a valgrind warning would be preferable. */ + db_iface_data->dbus_iface.name = nm_ref_string_new (dbus_iface_name); + db_iface_data->dbus_iface_is_wellknown = FALSE; + db_iface_data->iface_removed = FALSE; + c_list_link_tail (&dbobj->iface_lst_head, &db_iface_data->iface_lst); + } + + return db_iface_data; +} + static void -nm_client_init (NMClient *client) +nml_dbus_obj_iface_data_destroy (NMLDBusObjIfaceData *db_iface_data) { + guint i; + + nm_assert (db_iface_data); + nm_assert (c_list_is_empty (&db_iface_data->iface_lst)); + + if (db_iface_data->dbus_iface_is_wellknown) { + for (i = 0; i < db_iface_data->dbus_iface.meta->n_dbus_properties; i++) + nm_g_variant_unref (db_iface_data->prop_datas[i].prop_data_value); + } else + nm_ref_string_unref (db_iface_data->dbus_iface.name); + + g_free (db_iface_data); +} + +gpointer +nml_dbus_object_get_property_location (NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + const NMLDBusMetaProperty *meta_property) +{ + char *target_c; + + target_c = (char *) dbobj->nmobj; + if (meta_iface->base_struct_offset > 0) + target_c = *((gpointer *) (&target_c[meta_iface->base_struct_offset])); + return &target_c[meta_property->prop_struct_offset]; +} + +static void +nml_dbus_object_set_obj_state (NMLDBusObject *dbobj, + NMLDBusObjState obj_state, + NMClient *self) +{ + NMClientPrivate *priv; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NML_IS_DBUS_OBJECT (dbobj)); + +#if NM_MORE_ASSERTS > 10 + priv = NM_CLIENT_GET_PRIVATE (self); + switch (dbobj->obj_state) { + case NML_DBUS_OBJ_STATE_UNLINKED: nm_assert (c_list_is_empty (&dbobj->dbus_objects_lst)); break; + case NML_DBUS_OBJ_STATE_WATCHED_ONLY: nm_assert (c_list_contains (&priv->dbus_objects_lst_head_watched_only, &dbobj->dbus_objects_lst)); break; + case NML_DBUS_OBJ_STATE_ON_DBUS: nm_assert (c_list_contains (&priv->dbus_objects_lst_head_on_dbus, &dbobj->dbus_objects_lst)); break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY: nm_assert (c_list_contains (&priv->dbus_objects_lst_head_with_nmobj_not_ready, &dbobj->dbus_objects_lst)); break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY: nm_assert (c_list_contains (&priv->dbus_objects_lst_head_with_nmobj_ready, &dbobj->dbus_objects_lst)); break; + } +#endif + + if (dbobj->obj_state == obj_state) + return; + + NML_NMCLIENT_LOG_T (self, "[%s]: set D-Bus object state %s", dbobj->dbus_path->str, nml_dbus_obj_state_to_string (obj_state)); + + priv = NM_CLIENT_GET_PRIVATE (self); + dbobj->obj_state = obj_state; + switch (obj_state) { + case NML_DBUS_OBJ_STATE_UNLINKED: + c_list_unlink (&dbobj->dbus_objects_lst); + c_list_unlink (&dbobj->obj_changed_lst); + dbobj->obj_changed_type = NML_DBUS_OBJ_CHANGED_TYPE_NONE; + break; + case NML_DBUS_OBJ_STATE_WATCHED_ONLY: nm_c_list_move_tail (&priv->dbus_objects_lst_head_watched_only, &dbobj->dbus_objects_lst); break; + case NML_DBUS_OBJ_STATE_ON_DBUS: nm_c_list_move_tail (&priv->dbus_objects_lst_head_on_dbus, &dbobj->dbus_objects_lst); break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY: nm_c_list_move_tail (&priv->dbus_objects_lst_head_with_nmobj_not_ready, &dbobj->dbus_objects_lst); break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY: nm_c_list_move_tail (&priv->dbus_objects_lst_head_with_nmobj_ready, &dbobj->dbus_objects_lst); break; + default: + nm_assert_not_reached (); + } +} + +/*****************************************************************************/ + +static void +nml_dbus_object_obj_changed_link (NMClient *self, + NMLDBusObject *dbobj, + NMLDBusObjChangedType changed_type) +{ + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NML_IS_DBUS_OBJECT (dbobj)); + nm_assert (changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + + if (!NM_FLAGS_ALL ((NMLDBusObjChangedType ) dbobj->obj_changed_type, changed_type)) + NML_NMCLIENT_LOG_T (self, "[%s] changed-type 0x%02x linked", dbobj->dbus_path->str, (guint) changed_type); + + if (dbobj->obj_changed_type == NML_DBUS_OBJ_CHANGED_TYPE_NONE) { + NMClientPrivate *priv; + + /* We set the changed-type flag. Need to queue the object in the + * changed list. */ + nm_assert (c_list_is_empty (&dbobj->obj_changed_lst)); + priv = NM_CLIENT_GET_PRIVATE (self); + c_list_link_tail (&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + } else { + /* The object has changes flags and must be linked already. Note that + * this may be priv->obj_changed_lst_head, or a temporary list on the + * stack. + * + * This dance with the temporary list is done to ensure we can enqueue + * objects while we process the changes. */ + nm_assert (!c_list_is_empty (&dbobj->obj_changed_lst)); + } + + dbobj->obj_changed_type |= changed_type; + + nm_assert (NM_FLAGS_ALL (dbobj->obj_changed_type, changed_type)); +} + +static NMLDBusObjChangedType +nml_dbus_object_obj_changed_consume (NMClient *self, + NMLDBusObject *dbobj, + NMLDBusObjChangedType changed_type) +{ + NMClientPrivate *priv; + NMLDBusObjChangedType changed_type_res; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NML_IS_DBUS_OBJECT (dbobj)); + nm_assert (changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + nm_assert (dbobj->obj_changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + nm_assert (!c_list_is_empty (&dbobj->obj_changed_lst)); + + changed_type_res = dbobj->obj_changed_type & changed_type; + + dbobj->obj_changed_type &= ~changed_type; + + if (dbobj->obj_changed_type == NML_DBUS_OBJ_CHANGED_TYPE_NONE) { + c_list_unlink (&dbobj->obj_changed_lst); + nm_assert (changed_type_res != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + NML_NMCLIENT_LOG_T (self, "[%s] changed-type 0x%02x consumed", dbobj->dbus_path->str, (guint) changed_type_res); + return changed_type_res; + } + + priv = NM_CLIENT_GET_PRIVATE (self); + + nm_assert (!c_list_contains (&priv->obj_changed_lst_head, &dbobj->obj_changed_lst)); + nm_c_list_move_tail (&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + NML_NMCLIENT_LOG_T (self, "[%s] changed-type 0x%02x consumed (still has 0x%02x)", dbobj->dbus_path->str, (guint) changed_type_res, (guint) dbobj->obj_changed_type); + return changed_type_res; +} + +static gboolean +nml_dbus_object_obj_changed_any_linked (NMClient *self, + NMLDBusObjChangedType changed_type) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMLDBusObject *dbobj; + + nm_assert (changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + + c_list_for_each_entry (dbobj, &priv->obj_changed_lst_head, obj_changed_lst) { + nm_assert (dbobj->obj_changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + if (NM_FLAGS_ANY (dbobj->obj_changed_type, changed_type)) + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ + +static void +_dbobjs_notify_watchers_for_dbobj (NMClient *self, + NMLDBusObject *dbobj) +{ + NMLDBusObjWatcher *obj_watcher; + NMLDBusObjWatcher *obj_watcher_safe; + + c_list_for_each_entry_safe (obj_watcher, obj_watcher_safe, &dbobj->watcher_lst_head, _priv.watcher_lst) + obj_watcher->_priv.notify_fcn (self, obj_watcher); } static gboolean -_nm_client_check_nm_running (NMClient *client, GError **error) +_dbobjs_check_dbobj_ready (NMClient *self, + NMLDBusObject *dbobj) { - if (nm_client_get_nm_running (client)) + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NML_IS_DBUS_OBJECT (dbobj)); + nm_assert (G_IS_OBJECT (dbobj->nmobj)); + nm_assert ( NM_IS_OBJECT (dbobj->nmobj) + || NM_IS_CLIENT (dbobj->nmobj)); + nm_assert (NM_IN_SET ((NMLDBusObjState) dbobj->obj_state, NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)); + + if (G_LIKELY (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)) return TRUE; - else { - g_set_error_literal (error, - NM_CLIENT_ERROR, - NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, - "NetworkManager is not running"); + + if (!NM_OBJECT_GET_CLASS (dbobj->nmobj)->is_ready (NM_OBJECT (dbobj->nmobj))) + return FALSE; + + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, self); + + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + _dbobjs_notify_watchers_for_dbobj (self, dbobj); + + return TRUE; +} + +void +_nm_client_notify_object_changed (NMClient *self, + NMLDBusObject *dbobj) +{ + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + _dbobjs_notify_watchers_for_dbobj (self, dbobj); +} + +/*****************************************************************************/ + +static NMLDBusObject * +_dbobjs_dbobj_get_r (NMClient *self, + NMRefString *dbus_path_r) +{ + nm_assert (NM_IS_REF_STRING (dbus_path_r)); + + return g_hash_table_lookup (NM_CLIENT_GET_PRIVATE (self)->dbus_objects, &dbus_path_r); +} + +static NMLDBusObject * +_dbobjs_dbobj_get_s (NMClient *self, + const char *dbus_path) +{ + nm_auto_ref_string NMRefString *dbus_path_r = NULL; + + nm_assert (dbus_path); + dbus_path_r = nm_ref_string_new (dbus_path); + return _dbobjs_dbobj_get_r (self, dbus_path_r); +} + +static NMLDBusObject * +_dbobjs_dbobj_create (NMClient *self, + NMRefString *dbus_path_take) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer (&dbus_path_take); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMLDBusObject *dbobj; + + nm_assert (!_dbobjs_dbobj_get_r (self, dbus_path)); + + dbobj = nml_dbus_object_new (g_steal_pointer (&dbus_path)); + if (!g_hash_table_add (priv->dbus_objects, dbobj)) + nm_assert_not_reached (); + return dbobj; +} + +static NMLDBusObject * +_dbobjs_dbobj_get_or_create (NMClient *self, + NMRefString *dbus_path_take) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer (&dbus_path_take); + NMLDBusObject *dbobj; + + dbobj = _dbobjs_dbobj_get_r (self, dbus_path); + if (dbobj) + return dbobj; + return _dbobjs_dbobj_create (self, g_steal_pointer (&dbus_path)); +} + +static NMLDBusObject * +_dbobjs_get_nmobj (NMClient *self, + const char *dbus_path, + GType gtype) +{ + NMLDBusObject *dbobj; + + nm_assert ( gtype == G_TYPE_NONE + || g_type_is_a (gtype, NM_TYPE_OBJECT)); + + dbobj = _dbobjs_dbobj_get_s (self, dbus_path); + + if (!dbobj) + return NULL; + if (!dbobj->nmobj) + return NULL; + + if ( gtype != G_TYPE_NONE + && !g_type_is_a (G_OBJECT_TYPE (dbobj->nmobj), gtype)) + return NULL; + + return dbobj; +} + +static gpointer +_dbobjs_get_nmobj_unpack_visible (NMClient *self, + const char *dbus_path, + GType gtype) +{ + NMLDBusObject *dbobj; + + dbobj = _dbobjs_get_nmobj (self, dbus_path, gtype); + if (!dbobj) + return NULL; + if (dbobj->obj_state != NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY) + return NULL; + return dbobj->nmobj; +} + +/*****************************************************************************/ + +static gpointer +_dbobjs_obj_watcher_register_o (NMClient *self, + NMLDBusObject *dbobj, + NMLDBusObjWatchNotifyFcn notify_fcn, + gsize struct_size) +{ + NMLDBusObjWatcher *obj_watcher; + + nm_assert (NM_IS_CLIENT (self)); + _ASSERT_dbobj (dbobj, self); + nm_assert (notify_fcn); + nm_assert (struct_size > sizeof (NMLDBusObjWatcher)); + + obj_watcher = g_malloc (struct_size); + obj_watcher->dbobj = dbobj; + obj_watcher->_priv.notify_fcn = notify_fcn; + + /* we must enqueue the item in the front of the list. That is, because while + * invoking notify_fcn(), we iterate the watchers front-to-end. As we want to + * allow the callee to register new watches and unregister itself, this is + * the right way to do it. */ + c_list_link_front (&dbobj->watcher_lst_head, &obj_watcher->_priv.watcher_lst); + + return obj_watcher; +} + +static gpointer +_dbobjs_obj_watcher_register_r (NMClient *self, + NMRefString *dbus_path_take, + NMLDBusObjWatchNotifyFcn notify_fcn, + gsize struct_size) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer (&dbus_path_take); + NMLDBusObject *dbobj; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (notify_fcn); + + dbobj = _dbobjs_dbobj_get_or_create (self, g_steal_pointer (&dbus_path)); + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_UNLINKED) + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_WATCHED_ONLY, self); + return _dbobjs_obj_watcher_register_o (self, dbobj, notify_fcn, struct_size); +} + +static void +_dbobjs_obj_watcher_unregister (NMClient *self, + gpointer obj_watcher_base) +{ + NMLDBusObjWatcher *obj_watcher = obj_watcher_base; + NMLDBusObject *dbobj; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (obj_watcher); + nm_assert (NML_IS_DBUS_OBJECT (obj_watcher->dbobj)); + nm_assert (g_hash_table_lookup (NM_CLIENT_GET_PRIVATE (self)->dbus_objects, obj_watcher->dbobj) == obj_watcher->dbobj); + nm_assert (c_list_contains (&obj_watcher->dbobj->watcher_lst_head, &obj_watcher->_priv.watcher_lst)); + + c_list_unlink (&obj_watcher->_priv.watcher_lst); + + dbobj = obj_watcher->dbobj; + + g_free (obj_watcher); + + if ( c_list_is_empty (&dbobj->iface_lst_head) + && c_list_is_empty (&dbobj->watcher_lst_head)) { + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + NML_NMCLIENT_LOG_T (self, "[%s]: drop D-Bus watcher", dbobj->dbus_path->str); + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_UNLINKED, self); + if (!g_hash_table_steal (priv->dbus_objects, dbobj)) + nm_assert_not_reached (); + nml_dbus_object_unref (dbobj); + } +} + +/*****************************************************************************/ + +typedef struct { + NMLDBusObjWatcher parent; + NMLDBusPropertyO *pr_o; +} PropertyOData; + +gpointer +nml_dbus_property_o_get_obj (NMLDBusPropertyO *pr_o) +{ + nm_assert ( !pr_o->nmobj + || nml_dbus_property_o_is_ready (pr_o)); + return pr_o->nmobj; +} + +gboolean +nml_dbus_property_o_is_ready (const NMLDBusPropertyO *pr_o) +{ + return pr_o->is_ready + || !pr_o->owner_dbobj; +} + +static void +nml_dbus_property_o_notify_changed (NMLDBusPropertyO *pr_o, + NMClient *self) +{ + const NMLDBusPropertVTableO *vtable; + GObject *nmobj = NULL; + gboolean is_ready = TRUE; + gboolean changed_ready; + GType gtype; + + nm_assert (pr_o); + nm_assert (NM_IS_CLIENT (self)); + + if (!pr_o->owner_dbobj) + return; + + if (!pr_o->is_changed) { + if (pr_o->is_ready) + return; + goto done; + } + + pr_o->is_changed = FALSE; + + if (!pr_o->obj_watcher) + goto done; + + if (!pr_o->obj_watcher->dbobj->nmobj) { + if (pr_o->obj_watcher->dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS) { + NML_NMCLIENT_LOG_W (self, "[%s]: property %s references %s but object is not created", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str); + } else { + NML_NMCLIENT_LOG_E (self, "[%s]: property %s references %s but object is not present on D-Bus", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str); + } + goto done; + } + + vtable = pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].extra.property_vtable_o; + + gtype = vtable->get_o_type_fcn (); + if (!g_type_is_a (G_OBJECT_TYPE (pr_o->obj_watcher->dbobj->nmobj), gtype)) { + NML_NMCLIENT_LOG_E (self, "[%s]: property %s references %s with unexpected GObject type %s instead of %s", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str, + G_OBJECT_TYPE_NAME (pr_o->obj_watcher->dbobj->nmobj), + g_type_name (gtype)); + goto done; + } + + if (pr_o->obj_watcher->dbobj == pr_o->owner_dbobj) { + NML_NMCLIENT_LOG_W (self, "[%s]: property %s references itself", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name); + nmobj = pr_o->owner_dbobj->nmobj; + goto done; + } + + pr_o->block_is_changed = TRUE; + is_ready = _dbobjs_check_dbobj_ready (self, pr_o->obj_watcher->dbobj); + pr_o->block_is_changed = FALSE; + + if (!is_ready) { + is_ready = vtable->is_always_ready; + goto done; + } + + nmobj = pr_o->obj_watcher->dbobj->nmobj; + +done: + changed_ready = FALSE; + if (!pr_o->is_ready && is_ready) { + pr_o->is_ready = TRUE; + changed_ready = TRUE; + } + if (pr_o->nmobj != nmobj) { + pr_o->nmobj = nmobj; + _nm_client_queue_notify_object (self, + pr_o->owner_dbobj->nmobj, + pr_o->meta_iface->obj_properties[pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].obj_properties_idx]); + } + if ( changed_ready + && pr_o->owner_dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + nml_dbus_object_obj_changed_link (self, pr_o->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +void +nml_dbus_property_o_notify_changed_many (NMLDBusPropertyO *ptr, + guint len, + NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_o_notify_changed (ptr++, self); +} + +static void +nml_dbus_property_o_notify_watch_cb (NMClient *self, + gpointer obj_watcher) +{ + PropertyOData *pr_o_data = obj_watcher; + NMLDBusPropertyO *pr_o = pr_o_data->pr_o; + + nm_assert (pr_o->obj_watcher == obj_watcher); + + if ( !pr_o->block_is_changed + && !pr_o->is_changed) { + pr_o->is_changed = TRUE; + nml_dbus_object_obj_changed_link (self, pr_o->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } +} + +static NMLDBusNotifyUpdatePropFlags +nml_dbus_property_o_notify (NMClient *self, + NMLDBusPropertyO *pr_o, + NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value) +{ + const char *dbus_path = NULL; + gboolean changed = FALSE; + + if (!pr_o->owner_dbobj) { + nm_assert (!pr_o->meta_iface); + nm_assert (pr_o->dbus_property_idx == 0); + nm_assert (!pr_o->is_ready); + pr_o->owner_dbobj = dbobj; + pr_o->meta_iface = meta_iface; + pr_o->dbus_property_idx = dbus_property_idx; + } else { + nm_assert (pr_o->owner_dbobj == dbobj); + nm_assert (pr_o->meta_iface == meta_iface); + nm_assert (pr_o->dbus_property_idx == dbus_property_idx); + } + + if (value) + dbus_path = nm_dbus_path_not_empty (g_variant_get_string (value, NULL)); + + if ( pr_o->obj_watcher + && ( !dbus_path + || !nm_streq (dbus_path, pr_o->obj_watcher->dbobj->dbus_path->str))) { + _dbobjs_obj_watcher_unregister (self, + g_steal_pointer (&pr_o->obj_watcher)); + changed = TRUE; + } + if ( !pr_o->obj_watcher + && dbus_path) { + pr_o->obj_watcher = _dbobjs_obj_watcher_register_r (self, + nm_ref_string_new (dbus_path), + nml_dbus_property_o_notify_watch_cb, + sizeof (PropertyOData)); + ((PropertyOData *) pr_o->obj_watcher)->pr_o = pr_o; + changed = TRUE; + } + + if ( changed + && !pr_o->is_changed) { + pr_o->is_changed = TRUE; + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +void +nml_dbus_property_o_clear (NMLDBusPropertyO *pr_o, + NMClient *self) +{ + if (pr_o->obj_watcher) { + nm_assert (NM_IS_CLIENT (self)); + _dbobjs_obj_watcher_unregister (self, + g_steal_pointer (&pr_o->obj_watcher)); + } + if ( pr_o->nmobj + && pr_o->owner_dbobj + && pr_o->owner_dbobj->nmobj) { + _nm_client_queue_notify_object (self, + pr_o->owner_dbobj->nmobj, + pr_o->meta_iface->obj_properties[pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].obj_properties_idx]); + } + pr_o->owner_dbobj = NULL; + pr_o->meta_iface = NULL; + pr_o->dbus_property_idx = 0; + pr_o->is_ready = FALSE; +} + +void +nml_dbus_property_o_clear_many (NMLDBusPropertyO *pr_o, + guint len, + NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_o_clear (pr_o++, self); +} + +/*****************************************************************************/ + +typedef struct _NMLDBusPropertyAOData { + NMLDBusObjWatcher obj_watcher; + NMLDBusPropertyAO *parent; + CList data_lst; + GObject *nmobj; + struct _NMLDBusPropertyAOData *changed_next; + bool is_ready:1; + bool is_notified:1; + bool is_changed:1; + bool block_is_changed:1; +} PropertyAOData; + +static void +_ASSERT_pr_ao (NMLDBusPropertyAO *pr_ao) +{ + nm_assert (pr_ao); + +#if NM_MORE_ASSERTS > 10 + if (pr_ao->owner_dbobj) { + guint n_not_ready = 0; + guint n_is_changed = 0; + guint n_is_changed_2; + PropertyAOData *pr_ao_data; + + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->is_changed) + n_is_changed++; + if (!pr_ao_data->is_ready) + n_not_ready++; + } + nm_assert (n_not_ready == pr_ao->n_not_ready); + + n_is_changed_2 = 0; + pr_ao_data = pr_ao->changed_head; + while (pr_ao_data) { + nm_assert (pr_ao_data->is_changed); + n_is_changed_2++; + pr_ao_data = pr_ao_data->changed_next; + } + nm_assert (n_is_changed == n_is_changed_2); + } +#endif +} + +static gboolean +nml_dbus_property_ao_notify_changed_ao (PropertyAOData *pr_ao_data, + NMClient *self, + gboolean is_added /* or else removed */) +{ + NMLDBusPropertyAO *pr_ao; + const NMLDBusPropertVTableAO *vtable; + + if (!pr_ao_data->nmobj) + return FALSE; + + nm_assert (pr_ao_data->is_ready); + + if (is_added) { + if (pr_ao_data->is_notified) + return FALSE; + pr_ao_data->is_notified = TRUE; + } else { + if (!pr_ao_data->is_notified) + return FALSE; + pr_ao_data->is_notified = FALSE; + } + + pr_ao = pr_ao_data->parent; + + vtable = pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].extra.property_vtable_ao; + + if (vtable->notify_changed_ao) + vtable->notify_changed_ao (pr_ao, self, NM_OBJECT (pr_ao_data->nmobj), is_added); + return TRUE; +} + +const GPtrArray * +nml_dbus_property_ao_get_objs_as_ptrarray (NMLDBusPropertyAO *pr_ao) +{ + if (!pr_ao->arr) { + PropertyAOData *pr_ao_data; + gsize n; + + n = 0; + if (pr_ao->owner_dbobj) { + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->nmobj) + n++; + } + } + + pr_ao->arr = g_ptr_array_new_full (n, g_object_unref); + if (pr_ao->owner_dbobj) { + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->nmobj) + g_ptr_array_add (pr_ao->arr, g_object_ref (pr_ao_data->nmobj)); + } + } + } + return pr_ao->arr; +} + +gboolean +nml_dbus_property_ao_is_ready (const NMLDBusPropertyAO *pr_ao) +{ + return pr_ao->n_not_ready == 0; +} + +static void +nml_dbus_property_ao_notify_changed (NMLDBusPropertyAO *pr_ao, + NMClient *self) +{ + gboolean changed_prop = FALSE; + gboolean changed_ready = FALSE; + PropertyAOData *pr_ao_data; + + nm_assert (NM_IS_CLIENT (self)); + _ASSERT_pr_ao (pr_ao); + + if (!pr_ao->owner_dbobj) + return; + + if (!pr_ao->is_changed) { + if (pr_ao->n_not_ready == 0) + return; + goto done; + } + + pr_ao->is_changed = FALSE; + + while (pr_ao->changed_head) { + const NMLDBusPropertVTableAO *vtable; + GObject *nmobj = NULL; + gboolean is_ready = TRUE; + GType gtype; + + pr_ao_data = g_steal_pointer (&pr_ao->changed_head); + nm_assert (pr_ao_data->is_changed); + + pr_ao->changed_head = pr_ao_data->changed_next; + pr_ao_data->is_changed = FALSE; + + if (!pr_ao_data->obj_watcher.dbobj->nmobj) { + if (pr_ao_data->obj_watcher.dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS) { + NML_NMCLIENT_LOG_W (self, "[%s]: property %s references %s but object is not created", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str); + } else { + NML_NMCLIENT_LOG_E (self, "[%s]: property %s references %s but object is not present on D-Bus", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str); + } + goto done_pr_ao_data; + } + + vtable = pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].extra.property_vtable_ao; + + gtype = vtable->get_o_type_fcn (); + if (!g_type_is_a (G_OBJECT_TYPE (pr_ao_data->obj_watcher.dbobj->nmobj), gtype)) { + NML_NMCLIENT_LOG_E (self, "[%s]: property %s references %s with unexpected GObject type %s instead of %s", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str, + G_OBJECT_TYPE_NAME (pr_ao_data->obj_watcher.dbobj->nmobj), + g_type_name (gtype)); + goto done_pr_ao_data; + } + + if (pr_ao_data->obj_watcher.dbobj == pr_ao->owner_dbobj) { + NML_NMCLIENT_LOG_W (self, "[%s]: property %s references itself", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name); + nmobj = pr_ao->owner_dbobj->nmobj; + goto done_pr_ao_data; + } + + pr_ao_data->block_is_changed = TRUE; + is_ready = _dbobjs_check_dbobj_ready (self, pr_ao_data->obj_watcher.dbobj); + pr_ao_data->block_is_changed = FALSE; + + if (!is_ready) { + is_ready = vtable->is_always_ready; + goto done_pr_ao_data; + } + + if ( vtable->check_nmobj_visible_fcn + && !vtable->check_nmobj_visible_fcn (pr_ao_data->obj_watcher.dbobj->nmobj)) { + is_ready = TRUE; + goto done_pr_ao_data; + } + + nmobj = pr_ao_data->obj_watcher.dbobj->nmobj; + +done_pr_ao_data: + + if ( !pr_ao_data->is_ready + && is_ready) { + nm_assert (pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + pr_ao_data->is_ready = TRUE; + changed_ready = TRUE; + } + + if (pr_ao_data->nmobj != nmobj) { + if (nml_dbus_property_ao_notify_changed_ao (pr_ao_data, self, FALSE)) + changed_prop = TRUE; + pr_ao_data->nmobj = nmobj; + } + + if (!pr_ao_data->is_notified) { + if (nml_dbus_property_ao_notify_changed_ao (pr_ao_data, self, TRUE)) + changed_prop = TRUE; + } + } + + _ASSERT_pr_ao (pr_ao); + +done: + if (changed_prop) { + nm_clear_pointer (&pr_ao->arr, g_ptr_array_unref); + _nm_client_queue_notify_object (self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties[pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + if ( changed_ready + && pr_ao->n_not_ready == 0 + && pr_ao->owner_dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + nml_dbus_object_obj_changed_link (self, pr_ao->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +void +nml_dbus_property_ao_notify_changed_many (NMLDBusPropertyAO *ptr, + guint len, + NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_ao_notify_changed (ptr++, self); +} + +static void +nml_dbus_property_ao_notify_watch_cb (NMClient *self, + gpointer obj_watcher) +{ + PropertyAOData *pr_ao_data = obj_watcher; + NMLDBusPropertyAO *pr_ao = pr_ao_data->parent; + + nm_assert (g_hash_table_lookup (pr_ao->hash, pr_ao_data) == pr_ao_data); + + if ( !pr_ao_data->block_is_changed + && !pr_ao_data->is_changed) { + pr_ao_data->is_changed = TRUE; + pr_ao_data->changed_next = pr_ao->changed_head; + pr_ao->changed_head = pr_ao_data; + if (!pr_ao->is_changed) { + pr_ao->is_changed = TRUE; + nml_dbus_object_obj_changed_link (self, pr_ao->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + } + + _ASSERT_pr_ao (pr_ao); +} + +static NMLDBusNotifyUpdatePropFlags +nml_dbus_property_ao_notify (NMClient *self, + NMLDBusPropertyAO *pr_ao, + NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value) +{ + CList stale_lst_head = C_LIST_INIT (stale_lst_head); + PropertyAOData *pr_ao_data; + gboolean changed_prop = FALSE; + gboolean changed_obj = FALSE; + + if (!pr_ao->owner_dbobj) { + nm_assert (!pr_ao->data_lst_head.next); + nm_assert (!pr_ao->data_lst_head.prev); + nm_assert (!pr_ao->hash); + nm_assert (!pr_ao->meta_iface); + nm_assert (pr_ao->dbus_property_idx == 0); + nm_assert (pr_ao->n_not_ready == 0); + nm_assert (!pr_ao->changed_head); + nm_assert (!pr_ao->is_changed); + + c_list_init (&pr_ao->data_lst_head); + pr_ao->hash = g_hash_table_new (nm_ppdirect_hash, nm_ppdirect_equal); + pr_ao->owner_dbobj = dbobj; + pr_ao->meta_iface = meta_iface; + pr_ao->dbus_property_idx = dbus_property_idx; + } else { + nm_assert (pr_ao->data_lst_head.next); + nm_assert (pr_ao->data_lst_head.prev); + nm_assert (pr_ao->hash); + nm_assert (pr_ao->meta_iface == meta_iface); + nm_assert (pr_ao->dbus_property_idx == dbus_property_idx); + } + + c_list_splice (&stale_lst_head, &pr_ao->data_lst_head); + + if (value) { + GVariantIter iter; + const char *path; + + g_variant_iter_init (&iter, value); + while (g_variant_iter_next (&iter, "&o", &path)) { + nm_auto_ref_string NMRefString *dbus_path_r = NULL; + gpointer p_dbus_path_1; + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (PropertyAOData, obj_watcher) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMLDBusObjWatcher, dbobj) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMLDBusObject, dbus_path) == 0); + + if (!nm_dbus_path_not_empty (path)) { + /* should not happen. Anyway, silently skip empty paths. */ + continue; + } + + dbus_path_r = nm_ref_string_new (path); + p_dbus_path_1 = &dbus_path_r; + pr_ao_data = g_hash_table_lookup (pr_ao->hash, &p_dbus_path_1); + + if (pr_ao_data) { + /* With this implementation we cannot track the same path multiple times. + * Of course, for none of the properties where we use this, the server + * should expose the same path more than once, so this limitation is fine + * (maybe even preferable to drop duplicates form NMClient's API). */ + nm_assert (pr_ao_data->obj_watcher.dbobj->dbus_path == dbus_path_r); + if ( !changed_prop + && pr_ao_data->is_notified) { + /* The order of a notified entry changed. That means, we need to signal + * a change of the property. This detection of a change is not always + * correct, in particular we might detect some changes when there were + * none. That's not a serious problem, and fixing it would be expensive + * to implement. */ + changed_prop = (c_list_first (&stale_lst_head) != &pr_ao_data->data_lst); + } + nm_c_list_move_tail (&pr_ao->data_lst_head, &pr_ao_data->data_lst); + } else { + pr_ao_data = _dbobjs_obj_watcher_register_r (self, + g_steal_pointer (&dbus_path_r), + nml_dbus_property_ao_notify_watch_cb, + sizeof (PropertyAOData)), + pr_ao_data->parent = pr_ao; + pr_ao_data->nmobj = NULL; + pr_ao_data->changed_next = NULL; + pr_ao_data->is_changed = TRUE; + pr_ao_data->block_is_changed = FALSE; + pr_ao_data->is_ready = FALSE; + pr_ao_data->is_notified = FALSE; + c_list_link_tail (&pr_ao->data_lst_head, &pr_ao_data->data_lst); + if (!g_hash_table_add (pr_ao->hash, pr_ao_data)) + nm_assert_not_reached (); + nm_assert (pr_ao->n_not_ready < G_MAXUINT); + pr_ao->n_not_ready++; + } + +#if NM_MORE_ASSERTS > 10 + { + nm_auto_ref_string NMRefString *p = nm_ref_string_new (path); + gpointer pp = &p; + + nm_assert (g_hash_table_lookup (pr_ao->hash, &pp) == pr_ao_data); + } +#endif + } + } + + pr_ao->changed_head = NULL; + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->is_changed) { + pr_ao_data->changed_next = pr_ao->changed_head; + pr_ao->changed_head = pr_ao_data; + changed_obj = TRUE; + } + } + + while ((pr_ao_data = c_list_first_entry (&stale_lst_head, PropertyAOData, data_lst))) { + changed_obj = TRUE; + c_list_unlink (&pr_ao_data->data_lst); + if (!g_hash_table_remove (pr_ao->hash, pr_ao_data)) + nm_assert_not_reached (); + if (!pr_ao_data->is_ready) { + nm_assert (pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + } else { + if (nml_dbus_property_ao_notify_changed_ao (pr_ao_data, self, FALSE)) + changed_prop = TRUE; + } + _dbobjs_obj_watcher_unregister (self, pr_ao_data); + } + + _ASSERT_pr_ao (pr_ao); + + if (changed_obj) { + pr_ao->is_changed = TRUE; + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + + if (changed_prop) { + nm_clear_pointer (&pr_ao->arr, g_ptr_array_unref); + _nm_client_queue_notify_object (self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties[pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +void +nml_dbus_property_ao_clear (NMLDBusPropertyAO *pr_ao, + NMClient *self) +{ + _ASSERT_pr_ao (pr_ao); + + if (!pr_ao->owner_dbobj) { + nm_assert (pr_ao->n_not_ready == 0); + nm_assert ( (!pr_ao->data_lst_head.next && !pr_ao->data_lst_head.prev) + || (pr_ao->data_lst_head.next == pr_ao->data_lst_head.prev)); + nm_assert (!pr_ao->hash); + nm_assert (!pr_ao->meta_iface); + nm_assert (pr_ao->dbus_property_idx == 0); + nm_assert (!pr_ao->is_changed); + } else { + PropertyAOData *pr_ao_data; + gboolean changed_prop = FALSE; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (pr_ao->data_lst_head.next); + nm_assert (pr_ao->data_lst_head.prev); + nm_assert (pr_ao->hash); + nm_assert (pr_ao->meta_iface); + + while ((pr_ao_data = c_list_first_entry (&pr_ao->data_lst_head, PropertyAOData, data_lst))) { + if (!pr_ao_data->is_ready) { + nm_assert (pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + } else { + if (nml_dbus_property_ao_notify_changed_ao (pr_ao_data, self, FALSE)) + changed_prop = TRUE; + } + c_list_unlink (&pr_ao_data->data_lst); + if (!g_hash_table_remove (pr_ao->hash, pr_ao_data)) + nm_assert_not_reached (); + _dbobjs_obj_watcher_unregister (self, pr_ao_data); + } + + nm_assert (c_list_is_empty (&pr_ao->data_lst_head)); + nm_assert (pr_ao->n_not_ready == 0); + nm_assert (g_hash_table_size (pr_ao->hash) == 0); + + if ( changed_prop + && pr_ao->owner_dbobj->nmobj) { + _nm_client_queue_notify_object (self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties[pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + + nm_assert (c_list_is_empty (&pr_ao->data_lst_head)); + nm_assert (pr_ao->n_not_ready == 0); + nm_assert (g_hash_table_size (pr_ao->hash) == 0); + nm_clear_pointer (&pr_ao->hash, g_hash_table_unref); + pr_ao->owner_dbobj = NULL; + pr_ao->meta_iface = NULL; + pr_ao->dbus_property_idx = 0; + pr_ao->data_lst_head.next = NULL; + pr_ao->data_lst_head.prev = NULL; + pr_ao->is_changed = FALSE; + } + + nm_clear_pointer (&pr_ao->arr, g_ptr_array_unref); +} + +void +nml_dbus_property_ao_clear_many (NMLDBusPropertyAO *pr_ao, + guint len, + NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_ao_clear (pr_ao++, self); +} + +/*****************************************************************************/ + +NMLDBusNotifyUpdatePropFlags +_nml_dbus_notify_update_prop_ignore (NMClient *self, + NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value) +{ + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +NMLDBusNotifyUpdatePropFlags +_nml_dbus_notify_update_prop_o (NMClient *self, + NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value) +{ + const char *path = NULL; + NMRefString **p_property; + + if (value) + path = g_variant_get_string (value, NULL); + + p_property = nml_dbus_object_get_property_location (dbobj, + meta_iface, + &meta_iface->dbus_properties[dbus_property_idx]); + + if (!nm_streq0 (nm_ref_string_get_str (*p_property), path)) { + nm_ref_string_unref (*p_property); + *p_property = nm_ref_string_new (path); + } + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +_obj_handle_dbus_prop_changes (NMClient *self, + NMLDBusObject *dbobj, + NMLDBusObjIfaceData *db_iface_data, + guint dbus_property_idx, + GVariant *value) +{ + const NMLDBusMetaIface *meta_iface = db_iface_data->dbus_iface.meta; + const NMLDBusMetaProperty *meta_property = &meta_iface->dbus_properties[dbus_property_idx]; + gpointer p_property; + const char *dbus_type_s; + const GParamSpec *param_spec; + NMLDBusNotifyUpdatePropFlags notify_update_prop_flags; + + nm_assert (G_IS_OBJECT (dbobj->nmobj)); + + if ( value + && !g_variant_is_of_type (value, meta_property->dbus_type)) { + NML_NMCLIENT_LOG_E (self, "[%s] property %s.%s expected of type \"%s\" but is \"%s\". Ignore", + dbobj->dbus_path->str, + meta_iface->dbus_iface_name, + meta_property->dbus_property_name, + (const char *) meta_property->dbus_type, + (const char *) g_variant_get_type (value)); + value = NULL; + } + + if (meta_property->use_notify_update_prop) { + notify_update_prop_flags = meta_property->notify_update_prop (self, + dbobj, + meta_iface, + dbus_property_idx, + value); + if (notify_update_prop_flags == NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE) + return; + + nm_assert (notify_update_prop_flags == NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY); + nm_assert (G_IS_OBJECT (dbobj->nmobj)); + nm_assert (meta_iface->obj_properties); + nm_assert (meta_property->obj_properties_idx > 0); + param_spec = meta_iface->obj_properties[meta_property->obj_properties_idx]; + goto notify; + } + + p_property = nml_dbus_object_get_property_location (dbobj, meta_iface, meta_property); + + dbus_type_s = (const char *) meta_property->dbus_type; + + nm_assert (G_IS_OBJECT (dbobj->nmobj)); + nm_assert (meta_iface->obj_properties); + nm_assert (meta_property->obj_properties_idx > 0); + param_spec = meta_iface->obj_properties[meta_property->obj_properties_idx]; + + notify_update_prop_flags = NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; + + switch (dbus_type_s[0]) { + case 'b': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((bool *) p_property) = g_variant_get_boolean (value); + else if (param_spec->value_type == G_TYPE_BOOLEAN) + *((bool *) p_property) = ((GParamSpecBoolean *) param_spec)->default_value; + else { + nm_assert_not_reached (); + *((bool *) p_property) = FALSE; + } + break; + case 'y': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((guint8 *) p_property) = g_variant_get_byte (value); + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((guint8 *) p_property) = 0; + } + break; + case 'q': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((guint16 *) p_property) = g_variant_get_uint16 (value); + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((guint16 *) p_property) = 0; + } + break; + case 'i': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((gint32 *) p_property) = g_variant_get_int32 (value); + else if (param_spec->value_type == G_TYPE_INT) + *((gint32 *) p_property) = ((GParamSpecInt *) param_spec)->default_value; + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((gint32 *) p_property) = 0; + } + break; + case 'u': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((guint32 *) p_property) = g_variant_get_uint32 (value); + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((guint32 *) p_property) = 0; + } + break; + case 'x': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((gint64 *) p_property) = g_variant_get_int64 (value); + else if (param_spec->value_type == G_TYPE_INT64) + *((gint64 *) p_property) = ((GParamSpecInt64 *) param_spec)->default_value; + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((gint64 *) p_property) = 0; + } + break; + case 't': + nm_assert (dbus_type_s[1] == '\0'); + if (value) + *((guint64 *) p_property) = g_variant_get_uint64 (value); + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((guint64 *) p_property) = 0; + } + break; + case 's': + nm_assert (dbus_type_s[1] == '\0'); + nm_clear_g_free ((char **) p_property); + if (value) + *((char **) p_property) = g_variant_dup_string (value, NULL); + else { + nm_assert (nm_utils_g_param_spec_is_default (param_spec)); + *((char **) p_property) = NULL; + } + break; + case 'o': + nm_assert (dbus_type_s[1] == '\0'); + notify_update_prop_flags = nml_dbus_property_o_notify (self, + p_property, + dbobj, + meta_iface, + dbus_property_idx, + value); + break; + case 'a': + switch (dbus_type_s[1]) { + case 'y': + nm_assert (dbus_type_s[2] == '\0'); + { + gconstpointer v; + gsize l; + GBytes *b = NULL; + + if (value) { + v = g_variant_get_fixed_array (value, &l, 1); + + if (l > 0) { + /* empty arrays are coerced to NULL. */ + b = g_bytes_new (v, l); + } + } + + nm_clear_pointer ((GBytes **) p_property, g_bytes_unref); + *((GBytes **) p_property) = b; + } + break; + case 's': + nm_assert (dbus_type_s[2] == '\0'); + nm_assert (param_spec->value_type == G_TYPE_STRV); + + g_strfreev (*((char ***) p_property)); + if (value) + *((char ***) p_property) = g_variant_dup_strv (value, NULL); + else + *((char ***) p_property) = NULL; + break; + case 'o': + nm_assert (dbus_type_s[2] == '\0'); + notify_update_prop_flags = nml_dbus_property_ao_notify (self, + p_property, + dbobj, + meta_iface, + dbus_property_idx, + value); + break; + default: + nm_assert_not_reached (); + } + break; + default: + nm_assert_not_reached (); + } + +notify: + if (NM_FLAGS_HAS (notify_update_prop_flags, NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY)) + g_object_notify_by_pspec (dbobj->nmobj, (GParamSpec *) param_spec); +} + +static void +_obj_handle_dbus_iface_changes (NMClient *self, + NMLDBusObject *dbobj, + NMLDBusObjIfaceData *db_iface_data) +{ + NMLDBusObjPropData *db_prop_data; + gboolean is_self = (G_OBJECT (self) == dbobj->nmobj); + gboolean is_removed; + gboolean type_compatible; + guint8 i_prop; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (is_self || NM_IS_OBJECT (dbobj->nmobj)); + + if (G_UNLIKELY (!db_iface_data->nmobj_checked)) { + db_iface_data->nmobj_checked = TRUE; + type_compatible = db_iface_data->dbus_iface.meta->get_type_fcn + && g_type_is_a (G_OBJECT_TYPE (dbobj->nmobj), db_iface_data->dbus_iface.meta->get_type_fcn ()); + db_iface_data->nmobj_compatible = type_compatible; + } else + type_compatible = db_iface_data->nmobj_compatible; + + if (!type_compatible) { + /* on D-Bus, we have this interface associate with the object, but apparently + * it is not compatible. This is either a bug, or NetworkManager exposed an + * unexpected interface on D-Bus object for which we create a certain NMObject + * type. */ + return; + } + + is_removed = c_list_is_empty (&db_iface_data->iface_lst); + + nm_assert ( is_removed + || !c_list_is_empty (&db_iface_data->changed_prop_lst_head)); + + _nm_client_queue_notify_object (self, dbobj->nmobj, NULL); + + if (is_removed) { + for (i_prop = 0; i_prop < db_iface_data->dbus_iface.meta->n_dbus_properties; i_prop++) { + _obj_handle_dbus_prop_changes (self, + dbobj, + db_iface_data, + i_prop, + NULL); + } + } else { + while ((db_prop_data = c_list_first_entry (&db_iface_data->changed_prop_lst_head, NMLDBusObjPropData, changed_prop_lst))) { + gs_unref_variant GVariant *prop_data_value = NULL; + + c_list_unlink (&db_prop_data->changed_prop_lst); + + nm_assert (db_prop_data >= db_iface_data->prop_datas); + nm_assert (db_prop_data < &db_iface_data->prop_datas[db_iface_data->dbus_iface.meta->n_dbus_properties]); + nm_assert (db_prop_data->prop_data_value); + + /* Currently NMLDBusObject forgets about the variant. Theoretically, it could cache + * it, but there is no need because we update the property in nmobj (which extracts and + * keeps the property value itself). + * + * Note that we only consume the variant here when we process it. + * That implies that we already created a NMObject for the dbobj + * instance. Unless that happens, we cache the last seen property values. */ + prop_data_value = g_steal_pointer (&db_prop_data->prop_data_value); + + i_prop = (db_prop_data - &db_iface_data->prop_datas[0]); + _obj_handle_dbus_prop_changes (self, + dbobj, + db_iface_data, + i_prop, + prop_data_value); + } + } +} + +static void +_obj_handle_dbus_changes (NMClient *self, + NMLDBusObject *dbobj) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMLDBusObjIfaceData *db_iface_data; + NMLDBusObjIfaceData *db_iface_data_safe; + gs_unref_object GObject *nmobj_unregistering = NULL; + + _ASSERT_dbobj (dbobj, self); + + /* In a first step we only remember all the changes that that a D-Bus message brings + * and queue the object to process them. + * + * Here (in step 2) we look at what changed on D-Bus and propagate those changes + * to the NMObject instance. + * + * Note that here we still must not emit any GObject signals. That follows later, + * and again if the object changes, we will just queue that we handle the changes + * later. */ + + c_list_for_each_entry_safe (db_iface_data, db_iface_data_safe, &dbobj->iface_lst_head, iface_lst) { + if (!db_iface_data->iface_removed) + continue; + c_list_unlink (&db_iface_data->iface_lst); + if ( db_iface_data->dbus_iface_is_wellknown + && dbobj->nmobj) + _obj_handle_dbus_iface_changes (self, dbobj, db_iface_data); + nml_dbus_obj_iface_data_destroy (db_iface_data); + } + + if ( G_UNLIKELY (!dbobj->nmobj) + && !c_list_is_empty (&dbobj->iface_lst_head)) { + + /* Try to create a NMObject for this D-Bus object. Note that we detect the type + * based on the interfaces that it has, and if we make a choice once, we don't + * change. That means, one D-Bus object can only be of one type. */ + + if (NM_IN_SET (dbobj->dbus_path, _dbus_path_nm, + _dbus_path_settings, + _dbus_path_dns_manager)) { + /* For the main types, we don't detect them based on the interfaces present, + * but on the path names. Of course, both should correspond anyway. */ + NML_NMCLIENT_LOG_T (self, "[%s]: register NMClient for D-Bus object", + dbobj->dbus_path->str); + dbobj->nmobj = G_OBJECT (self); + if (dbobj->dbus_path == _dbus_path_nm) { + nm_assert (!priv->dbobj_nm); + priv->dbobj_nm = dbobj; + } else if (dbobj->dbus_path == _dbus_path_settings) { + nm_assert (!priv->dbobj_settings); + priv->dbobj_settings = dbobj; + } else { + nm_assert (dbobj->dbus_path == _dbus_path_dns_manager); + nm_assert (!priv->dbobj_dns_manager); + priv->dbobj_dns_manager = dbobj; + } + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, self); + } else { + GType gtype = G_TYPE_NONE; + NMLDBusMetaInteracePrio curr_prio = NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_LOW - 1; + + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + nm_assert (!db_iface_data->iface_removed); + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->dbus_iface.meta->interface_prio <= curr_prio) + continue; + curr_prio = db_iface_data->dbus_iface.meta->interface_prio; + gtype = db_iface_data->dbus_iface.meta->get_type_fcn (); + } + if (gtype != G_TYPE_NONE) { + dbobj->nmobj = g_object_new (gtype, NULL); + + NML_NMCLIENT_LOG_T (self, "[%s]: register new NMObject "NM_HASH_OBFUSCATE_PTR_FMT" of type %s", + dbobj->dbus_path->str, + NM_HASH_OBFUSCATE_PTR (dbobj->nmobj), + g_type_name (gtype)); + + nm_assert (NM_IS_OBJECT (dbobj->nmobj)); + NM_OBJECT_GET_CLASS (dbobj->nmobj)->register_client (NM_OBJECT (dbobj->nmobj), self, dbobj); + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, self); + } + } + } + + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + nm_assert (!db_iface_data->iface_removed); + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (c_list_is_empty (&db_iface_data->changed_prop_lst_head)) + continue; + if (dbobj->nmobj) + _obj_handle_dbus_iface_changes (self, dbobj, db_iface_data); + } + + if ( c_list_is_empty (&dbobj->iface_lst_head) + && dbobj->nmobj) { + + if (dbobj->nmobj == G_OBJECT (self)) { + dbobj->nmobj = NULL; + NML_NMCLIENT_LOG_T (self, "[%s]: unregister NMClient from D-Bus object", + dbobj->dbus_path->str); + if (dbobj->dbus_path == _dbus_path_nm) { + nm_assert (priv->dbobj_nm == dbobj); + priv->dbobj_nm = NULL; + nml_dbus_property_o_clear_many (priv->nm.property_o, G_N_ELEMENTS (priv->nm.property_o), self); + nml_dbus_property_ao_clear_many (priv->nm.property_ao, G_N_ELEMENTS (priv->nm.property_ao), self); + } else if (dbobj->dbus_path == _dbus_path_settings) { + nm_assert (priv->dbobj_settings == dbobj); + priv->dbobj_settings = NULL; + nml_dbus_property_ao_clear (&priv->settings.connections, self); + } else { + nm_assert (dbobj->dbus_path == _dbus_path_dns_manager); + nm_assert (priv->dbobj_dns_manager == dbobj); + priv->dbobj_dns_manager = NULL; + } + } else { + nmobj_unregistering = g_steal_pointer (&dbobj->nmobj); + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_WATCHED_ONLY, self); + NML_NMCLIENT_LOG_T (self, "[%s]: unregister NMObject "NM_HASH_OBFUSCATE_PTR_FMT" of type %s", + dbobj->dbus_path->str, + NM_HASH_OBFUSCATE_PTR (nmobj_unregistering), + g_type_name (G_OBJECT_TYPE (nmobj_unregistering))); + NM_OBJECT_GET_CLASS (nmobj_unregistering)->unregister_client (NM_OBJECT (nmobj_unregistering), self, dbobj); + } + } + + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +/*****************************************************************************/ + +static void +_dbus_handle_obj_changed_nmobj (NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + NMLDBusObject *dbobj; + CList obj_changed_tmp_lst_head = C_LIST_INIT (obj_changed_tmp_lst_head); + + nm_assert (!nml_dbus_object_obj_changed_any_linked (self, ~NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + + /* First we notify all watchers that these objects changed. Note that we only do that + * here for the list before processing the changes below in a loop. That is, because + * processing changes can again enqueue changed objects, and we only want to want to + * notify watchers for the events that happened earlier (not repeatedly notify them). */ + c_list_splice (&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + while ((dbobj = c_list_first_entry (&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + nm_c_list_move_tail (&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + _dbobjs_notify_watchers_for_dbobj (self, dbobj); + } + +again: + + nm_assert (!nml_dbus_object_obj_changed_any_linked (self, ~NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + + c_list_splice (&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + + while ((dbobj = c_list_first_entry (&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + + if (!nml_dbus_object_obj_changed_consume (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)) { + nm_assert_not_reached (); + continue; + } + + if (!dbobj->nmobj) + continue; + + if (dbobj->nmobj == G_OBJECT (self)) { + if (dbobj == priv->dbobj_nm) { + nml_dbus_property_o_notify_changed_many (priv->nm.property_o, G_N_ELEMENTS (priv->nm.property_o), self); + nml_dbus_property_ao_notify_changed_many (priv->nm.property_ao, G_N_ELEMENTS (priv->nm.property_ao), self); + } else if (dbobj == priv->dbobj_settings) + nml_dbus_property_ao_notify_changed (&priv->settings.connections, self); + else + nm_assert (dbobj == priv->dbobj_dns_manager); + } else + NM_OBJECT_GET_CLASS (dbobj->nmobj)->obj_changed_notify (NM_OBJECT (dbobj->nmobj)); + + _dbobjs_check_dbobj_ready (self, dbobj); + } + + if (!c_list_is_empty (&priv->obj_changed_lst_head)) { + nm_assert (nml_dbus_object_obj_changed_any_linked (self, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + /* we got new changes enqueued. Need to check again. */ + goto again; + } +} + +static void +_dbus_handle_obj_changed_dbus (NMClient *self, + const char *log_context) +{ + NMClientPrivate *priv; + NMLDBusObject *dbobj; + CList obj_changed_tmp_lst_head = C_LIST_INIT (obj_changed_tmp_lst_head); + + priv = NM_CLIENT_GET_PRIVATE (self); + + /* We move the changed list onto a temporary list and consume that. + * Note that nml_dbus_object_obj_changed_consume() will move the object + * back to the original list if there are changes of another type. + * + * This is done so that we can enqueue more changes while processing the + * change list. */ + c_list_splice (&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + + while ((dbobj = c_list_first_entry (&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + nm_auto_unref_nml_dbusobj NMLDBusObject *dbobj_unref = NULL; + + if (!nml_dbus_object_obj_changed_consume (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS)) + continue; + + nm_assert (dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS); + + dbobj_unref = nml_dbus_object_ref (dbobj); + + _obj_handle_dbus_changes (self, dbobj); + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_UNLINKED) + continue; + + if ( c_list_is_empty (&dbobj->iface_lst_head) + && c_list_is_empty (&dbobj->watcher_lst_head)) { + NML_NMCLIENT_LOG_T (self, "[%s]: drop D-Bus instance", dbobj->dbus_path->str); + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_UNLINKED, self); + if (!g_hash_table_steal (priv->dbus_objects, dbobj)) + nm_assert_not_reached (); + nml_dbus_object_unref (dbobj); + } + } + + /* D-Bus changes can only be enqueued in an earlier stage. We don't expect + * anymore changes of type D-Bus at this point. */ + nm_assert (!nml_dbus_object_obj_changed_any_linked (self, NML_DBUS_OBJ_CHANGED_TYPE_DBUS)); +} + +static void +_dbus_handle_changes_commit (NMClient *self, + gboolean allow_init_start_check_complete) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + _dbus_handle_obj_changed_nmobj (self); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->main_context); + + _nm_client_notify_event_emit (self); + + _set_nm_running (self); + + if (allow_init_start_check_complete) + _init_start_check_complete (self); +} + +static void +_dbus_handle_changes (NMClient *self, + const char *log_context, + gboolean allow_init_start_check_complete) +{ + _dbus_handle_obj_changed_dbus (self, log_context); + _dbus_handle_changes_commit (self, allow_init_start_check_complete); +} + +static gboolean +_dbus_handle_properties_changed (NMClient *self, + const char *log_context, + const char *object_path, + const char *interface_name, + gboolean allow_add_iface, + GVariant *changed_properties, + NMLDBusObject **inout_dbobj) +{ + NMLDBusObject *dbobj = NULL; + NMLDBusObjIfaceData *db_iface_data = NULL; + nm_auto_ref_string NMRefString *dbus_path = NULL; + + nm_assert (!changed_properties || g_variant_is_of_type (changed_properties, G_VARIANT_TYPE ("a{sv}"))); + + { + gs_free char *ss = NULL; + + NML_NMCLIENT_LOG_T (self, "[%s]: %s: properties changed for interface %s { %s }", + object_path, log_context, interface_name, + (ss = g_variant_print (changed_properties, TRUE))); + } + + if (inout_dbobj) { + dbobj = *inout_dbobj; + nm_assert (!dbobj || nm_streq (object_path, dbobj->dbus_path->str)); + } + if (!dbobj) { + dbus_path = nm_ref_string_new (object_path); + dbobj = _dbobjs_dbobj_get_r (self, dbus_path); + } + + if (dbobj) + db_iface_data = nml_dbus_object_iface_data_get (dbobj, interface_name, allow_add_iface); + else if (allow_add_iface) { + dbobj = _dbobjs_dbobj_create (self, g_steal_pointer (&dbus_path)); + nml_dbus_object_set_obj_state (dbobj, NML_DBUS_OBJ_STATE_ON_DBUS, self); + db_iface_data = nml_dbus_object_iface_data_get (dbobj, interface_name, TRUE); + } + + NM_SET_OUT (inout_dbobj, dbobj); + + if (!db_iface_data) { + if (allow_add_iface) + NML_NMCLIENT_LOG_E (self, "%s: [%s] too many interfaces on object. Something is very wrong", log_context, object_path); + else + NML_NMCLIENT_LOG_E (self, "%s: [%s] property changed signal for non existing interface %s", log_context, object_path, interface_name); + nm_assert ( !dbobj + || dbobj->obj_state != NML_DBUS_OBJ_STATE_UNLINKED); return FALSE; } + + if (!db_iface_data->dbus_iface_is_wellknown) + NML_NMCLIENT_LOG_W (self, "%s: [%s] ignore unknown interface %s", log_context, object_path, interface_name); + else if (changed_properties) { + GVariantIter iter_prop; + const char *property_name; + GVariant *property_value_tmp; + + g_variant_iter_init (&iter_prop, changed_properties); + while (g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value_tmp)) { + _nm_unused gs_unref_variant GVariant *property_value = property_value_tmp; + const NMLDBusMetaProperty *meta_property; + NMLDBusObjPropData *db_propdata; + guint property_idx; + + meta_property = nml_dbus_meta_property_get (db_iface_data->dbus_iface.meta, property_name, &property_idx); + if (!meta_property) { + NML_NMCLIENT_LOG_W (self, "%s: [%s]: ignore unknown property %s.%s", log_context, object_path, interface_name, property_name); + continue; + } + + db_propdata = &db_iface_data->prop_datas[property_idx]; + + NML_NMCLIENT_LOG_T (self, "[%s]: %s: %s property %s.%s", + object_path, log_context, + db_propdata->prop_data_value ? "update" : "set", + interface_name, property_name); + + nm_g_variant_unref (db_propdata->prop_data_value); + db_propdata->prop_data_value = g_steal_pointer (&property_value); + nm_c_list_move_tail (&db_iface_data->changed_prop_lst_head, &db_propdata->changed_prop_lst); + } + } + + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + return TRUE; +} + +static gboolean +_dbus_handle_interface_added (NMClient *self, + const char *log_context, + const char *object_path, + GVariant *ifaces) +{ + gboolean changed = FALSE; + const char *interface_name; + GVariant *changed_properties; + GVariantIter iter_ifaces; + NMLDBusObject *dbobj = NULL; + + nm_assert (g_variant_is_of_type (ifaces, G_VARIANT_TYPE ("a{sa{sv}}"))); + + g_variant_iter_init (&iter_ifaces, ifaces); + while (g_variant_iter_next (&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) { + _nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties; + + if (_dbus_handle_properties_changed (self, log_context, object_path, interface_name, TRUE, changed_properties, &dbobj)) + changed = TRUE; + } + + return changed; +} + +static gboolean +_dbus_handle_interface_removed (NMClient *self, + const char *log_context, + const char *object_path, + NMLDBusObject **inout_dbobj, + const char *const*removed_interfaces) +{ + gboolean changed = FALSE; + NMLDBusObject *dbobj; + gsize i; + + if ( inout_dbobj + && *inout_dbobj) { + dbobj = *inout_dbobj; + nm_assert (dbobj == _dbobjs_dbobj_get_s (self, object_path)); + } else { + dbobj = _dbobjs_dbobj_get_s (self, object_path); + if (!dbobj) { + NML_NMCLIENT_LOG_E (self, "%s: [%s]: receive interface removed event for non existing object", log_context, object_path); + return FALSE; + } + NM_SET_OUT (inout_dbobj, dbobj); + } + + for (i = 0; removed_interfaces[i]; i++) { + NMLDBusObjIfaceData *db_iface_data; + const char *interface_name = removed_interfaces[i]; + + db_iface_data = nml_dbus_object_iface_data_get (dbobj, interface_name, FALSE); + if (!db_iface_data) { + NML_NMCLIENT_LOG_E (self, "%s: [%s] receive interface remove event for unexpected interface %s", log_context, object_path, interface_name); + continue; + } + + NML_NMCLIENT_LOG_T (self, "%s: [%s] receive interface remove event for interface %s", log_context, object_path, interface_name); + db_iface_data->iface_removed = TRUE; + changed = TRUE; + } + + if (changed) + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + + return changed; +} + +static void +_dbus_managed_objects_changed_cb (const char *object_path, + GVariant *added_interfaces_and_properties, + const char *const*removed_interfaces, + gpointer user_data) +{ + NMClient *self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + const char *log_context; + gboolean changed; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!added_interfaces_and_properties) { + log_context = "interfaces-removed"; + changed = _dbus_handle_interface_removed (self, log_context, object_path, NULL, removed_interfaces); + } else { + log_context = "interfaces-added"; + changed = _dbus_handle_interface_added (self, log_context, object_path, added_interfaces_and_properties); + } + + if (changed) + _dbus_handle_changes (self, log_context, TRUE); +} + +static void +_dbus_properties_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMClient *self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + const char *interface_name; + gs_unref_variant GVariant *changed_properties = NULL; + gs_free const char **invalidated_properties = NULL; + const char *log_context = "properties-changed"; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) + return; + + g_variant_get (parameters, + "(&s@a{sv}^a&s)", + &interface_name, + &changed_properties, + &invalidated_properties); + + if (invalidated_properties && invalidated_properties[0]) { + NML_NMCLIENT_LOG_W (self, "%s: [%s] ignore invalidated properties on interface %s", + log_context, object_path, interface_name); + } + + if (_dbus_handle_properties_changed (self, log_context, object_path, interface_name, FALSE, changed_properties, NULL)) + _dbus_handle_changes (self, log_context, TRUE); +} + +static void +_dbus_get_managed_objects_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + NMClient *self; + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *managed_objects = NULL; + gs_free_error GError *error = NULL; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack (user_data, &self, &context_busy_watcher); + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + + nm_assert ((!!ret) != (!!error)); + + if ( !ret + && nm_utils_error_is_cancelled (error, FALSE)) + return; + + priv = NM_CLIENT_GET_PRIVATE (self); + + if (ret) { + nm_assert (g_variant_is_of_type (ret, G_VARIANT_TYPE ("(a{oa{sa{sv}}})"))); + managed_objects = g_variant_get_child_value (ret, 0); + } + + g_clear_object (&priv->get_managed_objects_cancellable); + + if (!managed_objects) { + NML_NMCLIENT_LOG_D (self, "GetManagedObjects() call failed: %s", error->message); + /* hm, now that's odd. Maybe NetworkManager just quit and we are about to get + * a name-owner changed signal soon. Treat this as if we got no managed objects at all. */ + } else + NML_NMCLIENT_LOG_D (self, "GetManagedObjects() completed"); + + if (managed_objects) { + GVariantIter iter; + const char *object_path; + GVariant *ifaces_tmp; + + g_variant_iter_init (&iter, managed_objects); + while (g_variant_iter_next (&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces_tmp)) { + gs_unref_variant GVariant *ifaces = ifaces_tmp; + + _dbus_handle_interface_added (self, "get-managed-objects", object_path, ifaces); + } + } + + /* always call _dbus_handle_changes(), even if nothing changed. We need this to complete + * initialization. */ + _dbus_handle_changes (self, "get-managed-objects", TRUE); +} + +/*****************************************************************************/ + +static void +_nm_client_get_settings_call_cb (GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMRemoteConnection *remote_connection; + NMClient *self; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *settings = NULL; + NMLDBusObject *dbobj; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if ( !ret + && nm_utils_error_is_cancelled (error, FALSE)) + return; + + remote_connection = user_data; + + self = _nm_object_get_client (remote_connection); + + dbobj = _nm_object_get_dbobj (remote_connection); + + _ASSERT_dbobj (dbobj, self); + + if (!ret) { + NML_NMCLIENT_LOG_T (self, "[%s] GetSettings() completed with error: %s", + dbobj->dbus_path->str, + error->message); + } else { + NML_NMCLIENT_LOG_T (self, "[%s] GetSettings() completed with success", + dbobj->dbus_path->str); + g_variant_get (ret, + "(@a{sa{sv}})", + &settings); + } + + _nm_remote_settings_get_settings_commit (remote_connection, settings); + + _dbus_handle_changes_commit (self, TRUE); +} + +void +_nm_client_get_settings_call (NMClient *self, + NMLDBusObject *dbobj) +{ + GCancellable *cancellable; + + cancellable = _nm_remote_settings_get_settings_prepare (NM_REMOTE_CONNECTION (dbobj->nmobj)); + + _nm_client_dbus_call_simple (self, + cancellable, + dbobj->dbus_path->str, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSettings", + g_variant_new ("()"), + G_VARIANT_TYPE ("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _nm_client_get_settings_call_cb, + dbobj->nmobj); +} + +static void +_dbus_settings_updated_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMClient *self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + const char *log_context = "settings-updated"; + NMLDBusObject *dbobj; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()"))) + return; + + dbobj = _dbobjs_dbobj_get_s (self, object_path); + + if ( !dbobj + || !NM_IS_REMOTE_CONNECTION (dbobj->nmobj)) { + NML_NMCLIENT_LOG_W (self, "%s: [%s] ignore Updated signal for non-existing setting", + log_context, object_path); + return; + } + + NML_NMCLIENT_LOG_T (self, "%s: [%s] Updated signal received", + log_context, object_path); + + _nm_client_get_settings_call (self, dbobj); +} + +/*****************************************************************************/ + +static void +_dbus_nm_connection_active_state_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMClient *self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + const char *log_context = "active-connection-state-changed"; + NMLDBusObject *dbobj; + guint32 state; + guint32 reason; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) { + NML_NMCLIENT_LOG_E (self, "%s: [%s] ignore StateChanged signal with unexpected signature", + log_context, object_path); + return; + } + + dbobj = _dbobjs_dbobj_get_s (self, object_path); + + if ( !dbobj + || !NM_IS_ACTIVE_CONNECTION (dbobj->nmobj)) { + NML_NMCLIENT_LOG_E (self, "%s: [%s] ignore StateChanged signal for non-existing active connection", + log_context, object_path); + return; + } + + g_variant_get (parameters, "(uu)", &state, &reason); + + NML_NMCLIENT_LOG_T (self, "%s: [%s] StateChanged signal received", + log_context, object_path); + + _nm_active_connection_state_changed_commit (NM_ACTIVE_CONNECTION (dbobj->nmobj), + state, + reason); + + _dbus_handle_changes_commit (self, TRUE); +} + +/*****************************************************************************/ + +static void +_dbus_nm_vpn_connection_state_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMClient *self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + const char *log_context = "vpn-connection-state-changed"; + NMLDBusObject *dbobj; + guint32 state; + guint32 reason; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) { + NML_NMCLIENT_LOG_E (self, "%s: [%s] ignore VpnStateChanged signal with unexpected signature", + log_context, object_path); + return; + } + + dbobj = _dbobjs_dbobj_get_s (self, object_path); + + if ( !dbobj + || !NM_IS_VPN_CONNECTION (dbobj->nmobj)) { + NML_NMCLIENT_LOG_E (self, "%s: [%s] ignore VpnStateChanged signal for non-existing vpn connection", + log_context, object_path); + return; + } + + g_variant_get (parameters, "(uu)", &state, &reason); + + NML_NMCLIENT_LOG_T (self, "%s: [%s] VpnStateChanged signal received", + log_context, object_path); + + _nm_vpn_connection_state_changed_commit (NM_VPN_CONNECTION (dbobj->nmobj), + state, + reason); + + _dbus_handle_changes_commit (self, TRUE); +} + +/*****************************************************************************/ + +static void +_emit_permissions_changed (NMClient *self, + GHashTable *permissions, + gboolean force_unknown) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + + if (!permissions) + return; + if (self->obj_base.is_disposing) + return; + + g_hash_table_iter_init (&iter, permissions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_signal_emit (self, + signals[PERMISSION_CHANGED], + 0, + GPOINTER_TO_UINT (key), + force_unknown + ? (guint) NM_CLIENT_PERMISSION_NONE + : GPOINTER_TO_UINT (value)); + } +} + + +static void +_dbus_check_permissions_start_cb (GObject *source, GAsyncResult *result, gpointer user_data) +{ + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + NMClient *self; + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + nm_auto_free_variant_iter GVariantIter *v_permissions = NULL; + gs_unref_hashtable GHashTable *old_permissions = NULL; + gs_free_error GError *error = NULL; + const char *pkey; + const char *pvalue; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if ( !ret + && nm_utils_error_is_cancelled (error, FALSE)) + return; + + self = user_data; + priv = NM_CLIENT_GET_PRIVATE (self); + + g_clear_object (&priv->permissions_cancellable); + + if (!ret) { + NML_NMCLIENT_LOG_T (self, "GetPermissions call failed: %s", error->message); + return; + } + + NML_NMCLIENT_LOG_T (self, "GetPermissions call finished with success"); + + /* get list of old permissions for change notification */ + old_permissions = g_steal_pointer (&priv->permissions); + priv->permissions = g_hash_table_new (nm_direct_hash, NULL); + + g_variant_get (ret, "(a{ss})", &v_permissions); + while (g_variant_iter_next (v_permissions, "{&s&s}", &pkey, &pvalue)) { + NMClientPermission perm; + NMClientPermissionResult perm_result; + + perm = nm_permission_to_client (pkey); + if (perm == NM_CLIENT_PERMISSION_NONE) + continue; + + perm_result = nm_permission_result_to_client (pvalue); + + g_hash_table_insert (priv->permissions, + GUINT_TO_POINTER (perm), + GUINT_TO_POINTER (perm_result)); + if (old_permissions) { + g_hash_table_remove (old_permissions, + GUINT_TO_POINTER (perm)); + } + } + + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->main_context); + _emit_permissions_changed (self, priv->permissions, FALSE); + _emit_permissions_changed (self, old_permissions, TRUE); +} + +static void +_dbus_check_permissions_start (NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + nm_clear_g_cancellable (&priv->permissions_cancellable); + priv->permissions_cancellable = g_cancellable_new (); + + NML_NMCLIENT_LOG_T (self, "GetPermissions() call started..."); + + _nm_client_dbus_call_simple (self, + priv->permissions_cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "GetPermissions", + g_variant_new ("()"), + G_VARIANT_TYPE ("(a{ss})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _dbus_check_permissions_start_cb, + self); +} + +static void +_dbus_nm_check_permissions_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMClient *self = user_data; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()"))) { + NML_NMCLIENT_LOG_E (self, "ignore CheckPermissions signal with unexpected signature %s", + g_variant_get_type_string (parameters)); + return; + } + + _dbus_check_permissions_start (self); +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_connections_cb (NMLDBusPropertyAO *pr_ao, + NMClient *self, + NMObject *nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal (self, + G_OBJECT (self), + nmobj, + is_added, + 5, + is_added + ? signals[CONNECTION_ADDED] + : signals[CONNECTION_REMOVED]); +} + +static void +_property_ao_notify_changed_all_devices_cb (NMLDBusPropertyAO *pr_ao, + NMClient *self, + NMObject *nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal (self, + G_OBJECT (self), + nmobj, + is_added, + 6, + is_added + ? signals[ANY_DEVICE_ADDED] + : signals[ANY_DEVICE_REMOVED]); +} + +static void +_property_ao_notify_changed_devices_cb (NMLDBusPropertyAO *pr_ao, + NMClient *self, + NMObject *nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal (self, + G_OBJECT (self), + nmobj, + is_added, + 7, + is_added + ? signals[DEVICE_ADDED] + : signals[DEVICE_REMOVED]); +} + +static void +_property_ao_notify_changed_active_connections_cb (NMLDBusPropertyAO *pr_ao, + NMClient *self, + NMObject *nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal (self, + G_OBJECT (self), + nmobj, + is_added, + 8, + is_added + ? signals[ACTIVE_CONNECTION_ADDED] + : signals[ACTIVE_CONNECTION_REMOVED]); +} + +/*****************************************************************************/ + +typedef struct { + NMLDBusObjWatcherWithPtr *obj_watcher; + const char *op_name; + NMLDBusObject *dbobj; + GTask *task; + GVariant *extra_results; + gpointer result; + GType gtype; + gulong cancellable_id; +} RequestWaitData; + +static void +_request_wait_data_free (RequestWaitData *request_data) +{ + nm_assert (!request_data->obj_watcher); + nm_assert (request_data->cancellable_id == 0); + nm_assert (!request_data->task || G_IS_TASK (request_data->task)); + + nm_g_object_unref (request_data->task); + nm_g_object_unref (request_data->result); + nm_g_variant_unref (request_data->extra_results); + if (request_data->dbobj) + nml_dbus_object_unref (request_data->dbobj); + nm_g_slice_free (request_data); +} + +static void +_request_wait_task_return (RequestWaitData *request_data) +{ + gs_unref_object GTask *task = NULL; + + nm_assert (request_data); + nm_assert (G_IS_TASK (request_data->task)); + nm_assert (request_data->dbobj); + nm_assert (NM_IS_OBJECT (request_data->dbobj->nmobj)); + nm_assert (!request_data->result); + + task = g_steal_pointer (&request_data->task); + + request_data->result = g_object_ref (request_data->dbobj->nmobj); + nm_clear_g_signal_handler (g_task_get_cancellable (task), &request_data->cancellable_id); + nm_clear_pointer (&request_data->dbobj, nml_dbus_object_unref); + g_task_return_pointer (task, request_data, (GDestroyNotify) _request_wait_data_free); +} + +static gboolean +_request_wait_complete (NMClient *self, + RequestWaitData *request_data, + gboolean force_complete) +{ + NMLDBusObject *dbobj; + + nm_assert (request_data); + nm_assert (!request_data->result); + nm_assert (!request_data->obj_watcher); + nm_assert (request_data->dbobj); + + dbobj = request_data->dbobj; + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY) { + NML_NMCLIENT_LOG_D (self, "%s() succeeded with %s", request_data->op_name, dbobj->dbus_path->str); + nm_assert (G_TYPE_CHECK_INSTANCE_TYPE (dbobj->nmobj, request_data->gtype)); + _request_wait_task_return (request_data); + return TRUE; + } + + if ( force_complete + || dbobj->obj_state != NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) { + if (force_complete) + NML_NMCLIENT_LOG_D (self, "%s() succeeded with %s but object is in an unsuitable state", request_data->op_name, dbobj->dbus_path->str); + else + NML_NMCLIENT_LOG_W (self, "%s() succeeded with %s but object is in an unsuitable state", request_data->op_name, dbobj->dbus_path->str); + + g_task_return_error (request_data->task, g_error_new (NM_CLIENT_ERROR, + NM_CLIENT_ERROR_OBJECT_CREATION_FAILED, + _("request succeeded with %s but object is in an unsuitable state"), + dbobj->dbus_path->str)); + _request_wait_data_free (request_data); + return TRUE; + } + + return FALSE; +} + +static void +_request_wait_complete_cb (NMClient *self, + NMClientNotifyEventWithPtr *notify_event) +{ + _request_wait_complete (self, + notify_event->user_data, + TRUE); +} + +static void +_request_wait_obj_watcher_cb (NMClient *self, + gpointer obj_watcher_base) +{ + NMLDBusObjWatcherWithPtr *obj_watcher = obj_watcher_base; + RequestWaitData *request_data = obj_watcher->user_data; + NMLDBusObject *dbobj; + + dbobj = request_data->dbobj; + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + return; + + nm_assert (NM_IN_SET ((NMLDBusObjState) dbobj->obj_state, NML_DBUS_OBJ_STATE_WATCHED_ONLY, + NML_DBUS_OBJ_STATE_ON_DBUS, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)); + + _dbobjs_obj_watcher_unregister (self, g_steal_pointer (&request_data->obj_watcher)); + + nm_clear_g_signal_handler (g_task_get_cancellable (request_data->task), &request_data->cancellable_id); + + _nm_client_notify_event_queue_with_ptr (self, + NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER + 30, + _request_wait_complete_cb, + request_data); +} + +static void +_request_wait_cancelled_cb (GCancellable *cancellable, + gpointer user_data) +{ + RequestWaitData *request_data = user_data; + NMClient *self; + GError *error = NULL; + + nm_assert (cancellable == g_task_get_cancellable (request_data->task)); + + nm_utils_error_set_cancelled (&error, FALSE, NULL); + + self = g_task_get_source_object (request_data->task); + + nm_clear_g_signal_handler (cancellable, &request_data->cancellable_id); + + _dbobjs_obj_watcher_unregister (self, g_steal_pointer (&request_data->obj_watcher)); + + g_task_return_error (request_data->task, error); + + _request_wait_data_free (request_data); +} + +static void +_request_wait_start (GTask *task_take, + const char *op_name, + GType gtype, + const char *dbus_path, + GVariant *extra_results_take) +{ + NMClient *self; + gs_unref_object GTask *task = g_steal_pointer (&task_take); + RequestWaitData *request_data; + GCancellable *cancellable; + NMLDBusObject *dbobj; + + nm_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + + dbobj = _dbobjs_get_nmobj (self, dbus_path, gtype); + + if (!dbobj) { + NML_NMCLIENT_LOG_E (self, "%s() succeeded with %s but object does not exist", op_name, dbus_path); + g_task_return_error (task, g_error_new (NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + _("operation succeeded but object %s does not exist"), + dbus_path)); + return; + } + + request_data = g_slice_new (RequestWaitData); + *request_data = (RequestWaitData) { + .task = g_steal_pointer (&task), + .op_name = op_name, + .gtype = gtype, + .dbobj = nml_dbus_object_ref (dbobj), + .obj_watcher = NULL, + .extra_results = g_steal_pointer (&extra_results_take), + .result = NULL, + .cancellable_id = 0, + }; + + if (_request_wait_complete (self, request_data, FALSE)) + return; + + NML_NMCLIENT_LOG_T (self, "%s() succeeded with %s. Wait for object to become ready", op_name, dbobj->dbus_path->str); + + request_data->obj_watcher = _dbobjs_obj_watcher_register_o (self, + dbobj, + _request_wait_obj_watcher_cb, + sizeof (NMLDBusObjWatcherWithPtr)); + request_data->obj_watcher->user_data = request_data; + + cancellable = g_task_get_cancellable (request_data->task); + if (cancellable) { + gulong id; + + id = g_cancellable_connect (cancellable, + G_CALLBACK (_request_wait_cancelled_cb), + request_data, + NULL); + if (id == 0) { + /* the callback was invoked synchronously, which destroyed @request_data. + * We must not touch @info anymore. */ + } else + request_data->cancellable_id = id; + } +} + +static gpointer +_request_wait_finish (NMClient *client, + GAsyncResult *result, + gpointer source_tag, + GVariant **out_result, + GError **error) +{ + RequestWaitData *request_data = NULL; + gpointer r; + + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + g_return_val_if_fail (nm_g_task_is_valid (result, client, source_tag), NULL); + + request_data = g_task_propagate_pointer (G_TASK (result), error); + if (!request_data) { + NM_SET_OUT (out_result, NULL); + return NULL; + } + + nm_assert (NM_IS_OBJECT (request_data->result)); + + NM_SET_OUT (out_result, g_steal_pointer (&request_data->extra_results)); + r = g_steal_pointer (&request_data->result); + + nm_assert (NM_IS_OBJECT (r)); + + _request_wait_data_free (request_data); + return r; +} + +/*****************************************************************************/ + +/** + * nm_client_get_dbus_connection: + * @client: a #NMClient + * + * Gets the %GDBusConnection of the instance. This can be either passed when + * constructing the instance (as "dbus-connection" property), or it will be + * automatically initialized during async/sync init. + * + * Returns: (transfer none): the D-Bus connection of the client, or %NULL if none is set. + * + * Since: 1.22 + **/ +GDBusConnection * +nm_client_get_dbus_connection (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + return NM_CLIENT_GET_PRIVATE (client)->dbus_connection; +} + +/** + * nm_client_get_dbus_name_owner: + * @client: a #NMClient + * + * Returns: (transfer none): the current name owner of the D-Bus service of NetworkManager. + * + * Since: 1.22 + **/ +const char * +nm_client_get_dbus_name_owner (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + return NM_CLIENT_GET_PRIVATE (client)->name_owner; } /** @@ -214,10 +3797,7 @@ nm_client_get_version (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return NULL; - - return nm_manager_get_version (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.version; } /** @@ -233,10 +3813,7 @@ nm_client_get_state (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN); - if (!nm_client_get_nm_running (client)) - return NM_STATE_UNKNOWN; - - return nm_manager_get_state (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.state; } /** @@ -253,10 +3830,20 @@ nm_client_get_startup (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; + return NM_CLIENT_GET_PRIVATE (client)->nm.startup; +} + +static void +_set_nm_running (NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + gboolean nm_running; - return nm_manager_get_startup (NM_CLIENT_GET_PRIVATE (client)->manager); + nm_running = priv->name_owner && !priv->get_managed_objects_cancellable; + if (priv->nm_running != nm_running) { + priv->nm_running = nm_running; + _notify (self, PROP_NM_RUNNING); + } } /** @@ -272,7 +3859,23 @@ nm_client_get_nm_running (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - return NM_CLIENT_GET_PRIVATE (client)->manager != NULL; + return NM_CLIENT_GET_PRIVATE (client)->nm_running; +} + +/** + * nm_client_get_metered: + * @client: a #NMClient + * + * Returns: whether the default route is metered. + * + * Since: 1.22 + */ +NMMetered +nm_client_get_metered (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NM_METERED_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE (client)->nm.metered; } /** @@ -288,10 +3891,7 @@ nm_client_networking_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_networking_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.networking_enabled; } /** @@ -305,17 +3905,26 @@ nm_client_networking_get_enabled (NMClient *client) * all controlled interfaces are available for activation. * * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22, use nm_client_networking_set_enabled_async() or GDBusConnection **/ gboolean nm_client_networking_set_enabled (NMClient *client, gboolean enable, GError **error) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!_nm_client_check_nm_running (client, error)) - return FALSE; - - return nm_manager_networking_set_enabled (NM_CLIENT_GET_PRIVATE (client)->manager, - enable, error); + /* FIXME(libnm-async-api): add nm_client_networking_set_enabled_async(). */ + + return _nm_client_dbus_call_sync_void (client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "Enable", + g_variant_new ("(b)", enable), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); } /** @@ -331,10 +3940,7 @@ nm_client_wireless_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_wireless_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.wireless_enabled; } /** @@ -343,16 +3949,22 @@ nm_client_wireless_get_enabled (NMClient *client) * @enabled: %TRUE to enable wireless * * Enables or disables wireless devices. - **/ + * + * Deprecated: 1.22, use nm_client_wireless_set_enabled_async() or GDBusConnection + */ void nm_client_wireless_set_enabled (NMClient *client, gboolean enabled) { g_return_if_fail (NM_IS_CLIENT (client)); - if (!nm_client_get_nm_running (client)) - return; + /* FIXME(libnm-async-api): add nm_client_wireless_set_enabled_async(). */ - nm_manager_wireless_set_enabled (NM_CLIENT_GET_PRIVATE (client)->manager, enabled); + _nm_client_set_property_sync_legacy (client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WirelessEnabled", + "b", + enabled); } /** @@ -368,10 +3980,7 @@ nm_client_wireless_hardware_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_wireless_hardware_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.wireless_hardware_enabled; } /** @@ -387,10 +3996,7 @@ nm_client_wwan_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_wwan_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.wwan_enabled; } /** @@ -405,10 +4011,14 @@ nm_client_wwan_set_enabled (NMClient *client, gboolean enabled) { g_return_if_fail (NM_IS_CLIENT (client)); - if (!_nm_client_check_nm_running (client, NULL)) - return; + /* FIXME(libnm-async-api): add nm_client_wwan_set_enabled_async(). */ - nm_manager_wwan_set_enabled (NM_CLIENT_GET_PRIVATE (client)->manager, enabled); + _nm_client_set_property_sync_legacy (client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WwanEnabled", + "b", + enabled); } /** @@ -424,10 +4034,7 @@ nm_client_wwan_hardware_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_wwan_hardware_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.wwan_hardware_enabled; } /** @@ -437,16 +4044,15 @@ nm_client_wwan_hardware_get_enabled (NMClient *client) * Determines whether WiMAX is enabled. * * Returns: %TRUE if WiMAX is enabled + * + * Deprecated: 1.22 This function always returns FALSE because WiMax is no longer supported **/ gboolean nm_client_wimax_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_wimax_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return FALSE; } /** @@ -455,16 +4061,13 @@ nm_client_wimax_get_enabled (NMClient *client) * @enabled: %TRUE to enable WiMAX * * Enables or disables WiMAX devices. + * + * Deprecated: 1.22 This function does nothing because WiMax is no longer supported **/ void nm_client_wimax_set_enabled (NMClient *client, gboolean enabled) { g_return_if_fail (NM_IS_CLIENT (client)); - - if (!nm_client_get_nm_running (client)) - return; - - nm_manager_wimax_set_enabled (NM_CLIENT_GET_PRIVATE (client)->manager, enabled); } /** @@ -474,16 +4077,15 @@ nm_client_wimax_set_enabled (NMClient *client, gboolean enabled) * Determines whether the WiMAX hardware is enabled. * * Returns: %TRUE if the WiMAX hardware is enabled + * + * Deprecated: 1.22 This function always returns FALSE because WiMax is no longer supported **/ gboolean nm_client_wimax_hardware_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_wimax_hardware_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return FALSE; } /** @@ -503,10 +4105,7 @@ nm_client_connectivity_check_get_available (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_connectivity_check_get_available (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.connectivity_check_available; } /** @@ -524,10 +4123,7 @@ nm_client_connectivity_check_get_enabled (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - if (!nm_client_get_nm_running (client)) - return FALSE; - - return nm_manager_connectivity_check_get_enabled (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.connectivity_check_enabled; } /** @@ -546,10 +4142,14 @@ nm_client_connectivity_check_set_enabled (NMClient *client, gboolean enabled) { g_return_if_fail (NM_IS_CLIENT (client)); - if (!nm_client_get_nm_running (client)) - return; + /* FIXME(libnm-async-api): add nm_client_wireless_set_enabled_async(). */ - nm_manager_connectivity_check_set_enabled (NM_CLIENT_GET_PRIVATE (client)->manager, enabled); + _nm_client_set_property_sync_legacy (client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "ConnectivityCheckEnabled", + "b", + enabled); } /** @@ -568,10 +4168,7 @@ nm_client_connectivity_check_get_uri (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return NULL; - - return nm_manager_connectivity_check_get_uri (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.connectivity_check_uri; } /** @@ -585,20 +4182,43 @@ nm_client_connectivity_check_get_uri (NMClient *client) * Gets NetworkManager current logging level and domains. * * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22, use nm_client_get_logging_async() or GDBusConnection **/ gboolean -nm_client_get_logging (NMClient *client, char **level, char **domains, GError **error) +nm_client_get_logging (NMClient *client, + char **level, + char **domains, + GError **error) { + gs_unref_variant GVariant *ret = NULL; + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); g_return_val_if_fail (level == NULL || *level == NULL, FALSE); g_return_val_if_fail (domains == NULL || *domains == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (!_nm_client_check_nm_running (client, error)) + /* FIXME(libnm-async-api): add nm_client_get_logging_async(). */ + + ret = _nm_client_dbus_call_sync (client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "GetLogging", + g_variant_new ("()"), + G_VARIANT_TYPE ("(ss)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) return FALSE; - return nm_manager_get_logging (NM_CLIENT_GET_PRIVATE (client)->manager, - level, domains, error); + g_variant_get (ret, + "(ss)", + level, + domains); + return TRUE; } /** @@ -612,6 +4232,8 @@ nm_client_get_logging (NMClient *client, char **level, char **domains, GError ** * Sets NetworkManager logging level and/or domains. * * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22, use nm_client_set_logging_async() or GDBusConnection **/ gboolean nm_client_set_logging (NMClient *client, const char *level, const char *domains, GError **error) @@ -619,11 +4241,20 @@ nm_client_set_logging (NMClient *client, const char *level, const char *domains, g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (!_nm_client_check_nm_running (client, error)) - return FALSE; - - return nm_manager_set_logging (NM_CLIENT_GET_PRIVATE (client)->manager, - level, domains, error); + /* FIXME(libnm-async-api): add nm_client_set_logging_async(). */ + + return _nm_client_dbus_call_sync_void (client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "SetLogging", + g_variant_new ("(ss)", + level ?: "", + domains ?: ""), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); } /** @@ -639,12 +4270,19 @@ nm_client_set_logging (NMClient *client, const char *level, const char *domains, NMClientPermissionResult nm_client_get_permission_result (NMClient *client, NMClientPermission permission) { + NMClientPrivate *priv; + gpointer result; + g_return_val_if_fail (NM_IS_CLIENT (client), NM_CLIENT_PERMISSION_RESULT_UNKNOWN); - if (!nm_client_get_nm_running (client)) + priv = NM_CLIENT_GET_PRIVATE (client); + if ( !priv->permissions + || !g_hash_table_lookup_extended (priv->permissions, + GUINT_TO_POINTER (permission), + NULL, + &result)) return NM_CLIENT_PERMISSION_RESULT_UNKNOWN; - - return nm_manager_get_permission_result (NM_CLIENT_GET_PRIVATE (client)->manager, permission); + return GPOINTER_TO_UINT (result); } /** @@ -663,10 +4301,7 @@ nm_client_get_connectivity (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NM_CONNECTIVITY_UNKNOWN); - if (!nm_client_get_nm_running (client)) - return NM_CONNECTIVITY_UNKNOWN; - - return nm_manager_get_connectivity (NM_CLIENT_GET_PRIVATE (client)->manager); + return NM_CLIENT_GET_PRIVATE (client)->nm.connectivity; } /** @@ -683,39 +4318,52 @@ nm_client_get_connectivity (NMClient *client) * if you do not want to block. * * Returns: the (new) current connectivity state + * + * Deprecated: 1.22, use nm_client_check_connectivity_async() or GDBusConnection */ NMConnectivityState nm_client_check_connectivity (NMClient *client, GCancellable *cancellable, GError **error) { + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + guint32 connectivity; + g_return_val_if_fail (NM_IS_CLIENT (client), NM_CONNECTIVITY_UNKNOWN); - if (!_nm_client_check_nm_running (client, error)) + ret = _nm_client_dbus_call_sync (client, + cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckConnectivity", + g_variant_new ("()"), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) return NM_CONNECTIVITY_UNKNOWN; - return nm_manager_check_connectivity (NM_CLIENT_GET_PRIVATE (client)->manager, - cancellable, error); -} + g_variant_get (ret, + "(u)", + &connectivity); -static void -check_connectivity_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - NMConnectivityState connectivity; - GError *error = NULL; + /* upon receiving the synchronous response, we hack the NMClient state + * and update the property outside the ordered D-Bus messages (like + * "PropertiesChanged" signals). + * + * This is really ugly, we shouldn't do this. */ - connectivity = nm_manager_check_connectivity_finish (NM_MANAGER (object), - result, &error); - if (!error) - g_simple_async_result_set_op_res_gssize (simple, connectivity); - else - g_simple_async_result_take_error (simple, error); + priv = NM_CLIENT_GET_PRIVATE (client); + + if (priv->nm.connectivity != connectivity) { + priv->nm.connectivity = connectivity; + _notify (client, PROP_CONNECTIVITY); + } - g_simple_async_result_complete (simple); - g_object_unref (simple); + return connectivity; } /** @@ -736,22 +4384,23 @@ nm_client_check_connectivity_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_check_connectivity_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_check_connectivity_async (NM_CLIENT_GET_PRIVATE (client)->manager, - cancellable, check_connectivity_cb, simple); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + _nm_client_dbus_call (client, + client, + nm_client_check_connectivity_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckConnectivity", + g_variant_new ("()"), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); } /** @@ -770,16 +4419,20 @@ nm_client_check_connectivity_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; + gs_unref_variant GVariant *ret = NULL; + guint32 connectivity; g_return_val_if_fail (NM_IS_CLIENT (client), NM_CONNECTIVITY_UNKNOWN); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NM_CONNECTIVITY_UNKNOWN); + g_return_val_if_fail (nm_g_task_is_valid (client, result, nm_client_check_connectivity_async), NM_CONNECTIVITY_UNKNOWN); - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) + ret = g_task_propagate_pointer (G_TASK (result), error); + if (!ret) return NM_CONNECTIVITY_UNKNOWN; - return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple); + + g_variant_get (ret, + "(u)", + &connectivity); + return connectivity; } /** @@ -794,6 +4447,8 @@ nm_client_check_connectivity_finish (NMClient *client, * or cleared. * * Returns: %TRUE if the request was successful, %FALSE if it failed + * + * Deprecated: 1.22, use nm_client_save_hostname_async() or GDBusConnection **/ gboolean nm_client_save_hostname (NMClient *client, @@ -802,29 +4457,18 @@ nm_client_save_hostname (NMClient *client, GError **error) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - - if (!_nm_client_check_nm_running (client, error)) - return FALSE; - - return nm_remote_settings_save_hostname (NM_CLIENT_GET_PRIVATE (client)->settings, - hostname, cancellable, error); -} - -static void -save_hostname_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (nm_remote_settings_save_hostname_finish (NM_REMOTE_SETTINGS (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); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); + + return _nm_client_dbus_call_sync_void (client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "SaveHostname", + g_variant_new ("(s)", hostname ?: ""), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); } /** @@ -846,23 +4490,23 @@ nm_client_save_hostname_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_save_hostname_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_remote_settings_save_hostname_async (NM_CLIENT_GET_PRIVATE (client)->settings, - hostname, - cancellable, save_hostname_cb, simple); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + _nm_client_dbus_call (client, + client, + nm_client_save_hostname_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "SaveHostname", + g_variant_new ("(s)", hostname ?: ""), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); } /** @@ -880,20 +4524,14 @@ nm_client_save_hostname_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_save_hostname_async), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - else - return g_simple_async_result_get_op_res_gboolean (simple); + return g_task_propagate_boolean (G_TASK (result), error); } /*****************************************************************************/ -/* Devices */ +/* Devices */ /*****************************************************************************/ /** @@ -914,10 +4552,7 @@ nm_client_get_devices (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return ∅ - - return nm_manager_get_devices (NM_CLIENT_GET_PRIVATE (client)->manager); + return nml_dbus_property_ao_get_objs_as_ptrarray (&NM_CLIENT_GET_PRIVATE (client)->nm.property_ao[PROPERTY_AO_IDX_DEVICES]); } /** @@ -945,10 +4580,7 @@ nm_client_get_all_devices (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return ∅ - - return nm_manager_get_all_devices (NM_CLIENT_GET_PRIVATE (client)->manager); + return nml_dbus_property_ao_get_objs_as_ptrarray (&NM_CLIENT_GET_PRIVATE (client)->nm.property_ao[PROPERTY_AO_IDX_ALL_DEVICES]); } /** @@ -966,10 +4598,7 @@ nm_client_get_device_by_path (NMClient *client, const char *object_path) g_return_val_if_fail (NM_IS_CLIENT (client), NULL); g_return_val_if_fail (object_path, NULL); - if (!nm_client_get_nm_running (client)) - return NULL; - - return nm_manager_get_device_by_path (NM_CLIENT_GET_PRIVATE (client)->manager, object_path); + return _dbobjs_get_nmobj_unpack_visible (client, object_path, NM_TYPE_DEVICE); } /** @@ -984,13 +4613,21 @@ nm_client_get_device_by_path (NMClient *client, const char *object_path) NMDevice * nm_client_get_device_by_iface (NMClient *client, const char *iface) { + const GPtrArray *devices; + guint i; + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); g_return_val_if_fail (iface, NULL); - if (!nm_client_get_nm_running (client)) - return NULL; + devices = nm_client_get_devices (client); + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index (devices, i); - return nm_manager_get_device_by_iface (NM_CLIENT_GET_PRIVATE (client)->manager, iface); + if (nm_streq0 (nm_device_get_iface (candidate), iface)) + return candidate; + } + + return NULL; } /*****************************************************************************/ @@ -1012,10 +4649,7 @@ nm_client_get_active_connections (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return ∅ - - return nm_manager_get_active_connections (NM_CLIENT_GET_PRIVATE (client)->manager); + return nml_dbus_property_ao_get_objs_as_ptrarray (&NM_CLIENT_GET_PRIVATE (client)->nm.property_ao[PROPERTY_AO_IDX_ACTIVE_CONNECTIONS]); } /** @@ -1042,10 +4676,7 @@ nm_client_get_primary_connection (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return NULL; - - return nm_manager_get_primary_connection (NM_CLIENT_GET_PRIVATE (client)->manager); + return nml_dbus_property_o_get_obj (&NM_CLIENT_GET_PRIVATE (client)->nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION]); } /** @@ -1064,29 +4695,36 @@ nm_client_get_activating_connection (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return NULL; - - return nm_manager_get_activating_connection (NM_CLIENT_GET_PRIVATE (client)->manager); + return nml_dbus_property_o_get_obj (&NM_CLIENT_GET_PRIVATE (client)->nm.property_o[PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION]); } +/*****************************************************************************/ + static void -activate_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +activate_connection_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - GSimpleAsyncResult *simple = user_data; - NMActiveConnection *ac; + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *ret = NULL; + const char *v_active_connection; GError *error = NULL; - ac = nm_manager_activate_connection_finish (NM_MANAGER (object), result, &error); - if (ac) - g_simple_async_result_set_op_res_gpointer (simple, ac, g_object_unref); - else - g_simple_async_result_take_error (simple, error); + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled (error, FALSE)) + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + return; + } + + g_variant_get (ret, "(&o)", &v_active_connection); - g_simple_async_result_complete (simple); - g_object_unref (simple); + _request_wait_start (g_steal_pointer (&task), + "ActivateConnection", + NM_TYPE_ACTIVE_CONNECTION, + v_active_connection, + NULL); } /** @@ -1131,27 +4769,45 @@ nm_client_activate_connection_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; + const char *arg_connection = NULL; + const char *arg_device = NULL; g_return_if_fail (NM_IS_CLIENT (client)); - if (device) - g_return_if_fail (NM_IS_DEVICE (device)); - if (connection) + + if (connection) { g_return_if_fail (NM_IS_CONNECTION (connection)); + arg_connection = nm_connection_get_path (connection); + g_return_if_fail (arg_connection); + } - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; + if (device) { + g_return_if_fail (NM_IS_DEVICE (device)); + arg_device = nm_object_get_path (NM_OBJECT (device)); + g_return_if_fail (arg_device); } - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_activate_connection_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_activate_connection_async (NM_CLIENT_GET_PRIVATE (client)->manager, - connection, device, specific_object, - cancellable, activate_cb, simple); + NML_NMCLIENT_LOG_T (client, "ActivateConnection() for connection \"%s\", device \"%s\", specific_object \"%s", + arg_connection ?: "/", + arg_device ?: "/", + specific_object ?: "/"); + + _nm_client_dbus_call (client, + client, + nm_client_activate_connection_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "ActivateConnection", + g_variant_new ("(ooo)", + arg_connection ?: "/", + arg_device ?: "/", + specific_object ?: "/"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + activate_connection_cb); } /** @@ -1170,37 +4826,153 @@ nm_client_activate_connection_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; + return NM_ACTIVE_CONNECTION (_request_wait_finish (client, + result, + nm_client_activate_connection_async, + NULL, + error)); +} - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); +/*****************************************************************************/ - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - else - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); +static void +_add_and_activate_connection_done (GObject *object, + GAsyncResult *result, + gboolean use_add_and_activate_v2, + GTask *task_take) +{ + _nm_unused gs_unref_object GTask *task = task_take; + gs_unref_variant GVariant *ret = NULL; + GError *error = NULL; + gs_unref_variant GVariant *v_result = NULL; + const char *v_active_connection; + const char *v_path; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled (error, FALSE)) + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + return; + } + + if (use_add_and_activate_v2) { + g_variant_get (ret, + "(&o&o@a{sv})", + &v_path, + &v_active_connection, + &v_result); + } else { + g_variant_get (ret, + "(&o&o)", + &v_path, + &v_active_connection); + } + + _request_wait_start (g_steal_pointer (&task), + "AddAndActivateConnection", + NM_TYPE_ACTIVE_CONNECTION, + v_active_connection, + g_steal_pointer (&v_result)); } static void -add_activate_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +_add_and_activate_connection_v1_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - gs_unref_object GSimpleAsyncResult *simple = user_data; - gs_unref_variant GVariant *result_data = NULL; - gs_unref_object NMActiveConnection *ac = NULL; - GError *error = NULL; + _add_and_activate_connection_done (object, result, FALSE, user_data); +} - ac = nm_manager_add_and_activate_connection_finish (NM_MANAGER (object), result, &result_data, &error); - if (ac) { - g_simple_async_result_set_op_res_gpointer (simple, - _nm_activate_result_new (ac, result_data), - (GDestroyNotify) _nm_activate_result_free); - } else - g_simple_async_result_take_error (simple, error); +static void +_add_and_activate_connection_v2_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_and_activate_connection_done (object, result, TRUE, user_data); +} - g_simple_async_result_complete (simple); +static void +_add_and_activate_connection (NMClient *self, + gboolean is_v2, + NMConnection *partial, + NMDevice *device, + const char *specific_object, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant *arg_connection = NULL; + gboolean use_add_and_activate_v2 = FALSE; + const char *arg_device = NULL; + gpointer source_tag; + + g_return_if_fail (NM_IS_CLIENT (self)); + g_return_if_fail (!partial || NM_IS_CONNECTION (partial)); + + if (device) { + g_return_if_fail (NM_IS_DEVICE (device)); + arg_device = nm_object_get_path (NM_OBJECT (device)); + g_return_if_fail (arg_device); + } + + if (partial) + arg_connection = nm_connection_to_dbus (partial, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0); + + if (is_v2) { + if (!options) + options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); + use_add_and_activate_v2 = TRUE; + source_tag = nm_client_add_and_activate_connection2; + } else { + if (options) { + if (g_variant_n_children (options) > 0) + use_add_and_activate_v2 = TRUE; + else + nm_clear_pointer (&options, nm_g_variant_unref_floating); + } + source_tag = nm_client_add_and_activate_connection_async; + } + + NML_NMCLIENT_LOG_D (self, "AddAndActivateConnection() started..."); + + if (use_add_and_activate_v2) { + _nm_client_dbus_call (self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "AddAndActivateConnection2", + g_variant_new ("(@a{sa{sv}}oo@a{sv})", + arg_connection, + arg_device ?: "/", + specific_object ?: "/", + options), + G_VARIANT_TYPE ("(ooa{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_and_activate_connection_v2_cb); + } else { + _nm_client_dbus_call (self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "AddAndActivateConnection", + g_variant_new ("(@a{sa{sv}}oo)", + arg_connection, + arg_device ?: "/", + specific_object ?: "/"), + G_VARIANT_TYPE ("(oo)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_and_activate_connection_v1_cb); + } } /** @@ -1217,6 +4989,7 @@ add_activate_cb (GObject *object, * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can * get using nm_object_get_path(), and which will be used to complete the * details of the newly added connection. + * If the variant is floating, it will be consumed. * @cancellable: a #GCancellable, or %NULL * @callback: callback to be called when the activation has started * @user_data: caller-specific data passed to @callback @@ -1241,32 +5014,15 @@ nm_client_add_and_activate_connection_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - - g_return_if_fail (NM_IS_CLIENT (client)); - g_return_if_fail (NM_IS_DEVICE (device)); - if (partial) - g_return_if_fail (NM_IS_CONNECTION (partial)); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_add_and_activate_connection_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_add_and_activate_connection_async (NM_CLIENT_GET_PRIVATE (client)->manager, - partial, - device, - specific_object, - NULL, - FALSE, - cancellable, - add_activate_cb, - simple); + _add_and_activate_connection (client, + FALSE, + partial, + device, + specific_object, + NULL, + cancellable, + callback, + user_data); } /** @@ -1288,17 +5044,11 @@ nm_client_add_and_activate_connection_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - _NMActivateResult *r; - - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - r = g_simple_async_result_get_op_res_gpointer (simple); - return g_object_ref (r->active); + return NM_ACTIVE_CONNECTION (_request_wait_finish (client, + result, + nm_client_add_and_activate_connection_async, + NULL, + error)); } /** @@ -1354,32 +5104,15 @@ nm_client_add_and_activate_connection2 (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - - g_return_if_fail (NM_IS_CLIENT (client)); - g_return_if_fail (NM_IS_DEVICE (device)); - if (partial) - g_return_if_fail (NM_IS_CONNECTION (partial)); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_add_and_activate_connection2); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_add_and_activate_connection_async (NM_CLIENT_GET_PRIVATE (client)->manager, - partial, - device, - specific_object, - options, - TRUE, - cancellable, - add_activate_cb, - simple); + _add_and_activate_connection (client, + TRUE, + partial, + device, + specific_object, + options, + cancellable, + callback, + user_data); } /** @@ -1405,22 +5138,15 @@ nm_client_add_and_activate_connection2_finish (NMClient *client, GVariant **out_result, GError **error) { - GSimpleAsyncResult *simple; - _NMActivateResult *r; - - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) { - NM_SET_OUT (out_result, NULL); - return NULL; - } - r = g_simple_async_result_get_op_res_gpointer (simple); - NM_SET_OUT (out_result, nm_g_variant_ref (r->add_and_activate_output)); - return g_object_ref (r->active); + return NM_ACTIVE_CONNECTION (_request_wait_finish (client, + result, + nm_client_add_connection2, + out_result, + error)); } +/*****************************************************************************/ + /** * nm_client_deactivate_connection: * @client: a #NMClient @@ -1431,6 +5157,8 @@ nm_client_add_and_activate_connection2_finish (NMClient *client, * Deactivates an active #NMActiveConnection. * * Returns: success or failure + * + * Deprecated: 1.22, use nm_client_deactivate_connection_async() or GDBusConnection **/ gboolean nm_client_deactivate_connection (NMClient *client, @@ -1438,30 +5166,24 @@ nm_client_deactivate_connection (NMClient *client, GCancellable *cancellable, GError **error) { + const char *active_path; + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE); - if (!nm_client_get_nm_running (client)) - return TRUE; - - return nm_manager_deactivate_connection (NM_CLIENT_GET_PRIVATE (client)->manager, - active, cancellable, error); -} - -static void -deactivated_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (nm_manager_deactivate_connection_finish (NM_MANAGER (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); + active_path = nm_object_get_path (NM_OBJECT (active)); + g_return_val_if_fail (active_path, FALSE); + + return _nm_client_dbus_call_sync_void (client, + cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "DeactivateConnection", + g_variant_new ("(o)", active_path), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); } /** @@ -1481,26 +5203,28 @@ nm_client_deactivate_connection_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; + const char *active_path; g_return_if_fail (NM_IS_CLIENT (client)); g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active)); - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_deactivate_connection_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - - if (!_nm_client_check_nm_running (client, NULL)) { - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - - nm_manager_deactivate_connection_async (NM_CLIENT_GET_PRIVATE (client)->manager, - active, - cancellable, deactivated_cb, simple); + active_path = nm_object_get_path (NM_OBJECT (active)); + g_return_if_fail (active_path); + + _nm_client_dbus_call (client, + client, + nm_client_deactivate_connection_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "DeactivateConnection", + g_variant_new ("(o)", active_path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); } /** @@ -1518,16 +5242,10 @@ nm_client_deactivate_connection_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_deactivate_connection_async), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - else - return g_simple_async_result_get_op_res_gboolean (simple); + return g_task_propagate_boolean (G_TASK (result), error); } /*****************************************************************************/ @@ -1550,10 +5268,7 @@ nm_client_get_connections (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return ∅ - - return nm_remote_settings_get_connections (NM_CLIENT_GET_PRIVATE (client)->settings); + return nml_dbus_property_ao_get_objs_as_ptrarray (&NM_CLIENT_GET_PRIVATE (client)->settings.connections); } /** @@ -1572,13 +5287,20 @@ nm_client_get_connections (NMClient *client) NMRemoteConnection * nm_client_get_connection_by_id (NMClient *client, const char *id) { + const GPtrArray *arr; + guint i; + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (id, NULL); - if (!nm_client_get_nm_running (client)) - return NULL; + arr = nm_client_get_connections (client); + for (i = 0; i < arr->len; i++) { + NMRemoteConnection *c = NM_REMOTE_CONNECTION (arr->pdata[i]); - return nm_remote_settings_get_connection_by_id (NM_CLIENT_GET_PRIVATE (client)->settings, id); + if (nm_streq0 (id, nm_connection_get_id (NM_CONNECTION (c)))) + return c; + } + return NULL; } /** @@ -1600,10 +5322,7 @@ nm_client_get_connection_by_path (NMClient *client, const char *path) g_return_val_if_fail (NM_IS_CLIENT (client), NULL); g_return_val_if_fail (path != NULL, NULL); - if (!nm_client_get_nm_running (client)) - return NULL; - - return nm_remote_settings_get_connection_by_path (NM_CLIENT_GET_PRIVATE (client)->settings, path); + return _dbobjs_get_nmobj_unpack_visible (client, path, NM_TYPE_REMOTE_CONNECTION); } /** @@ -1622,64 +5341,150 @@ nm_client_get_connection_by_path (NMClient *client, const char *path) NMRemoteConnection * nm_client_get_connection_by_uuid (NMClient *client, const char *uuid) { + const GPtrArray *arr; + guint i; + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (uuid != NULL, NULL); + g_return_val_if_fail (uuid, NULL); - if (!nm_client_get_nm_running (client)) - return NULL; + arr = nm_client_get_connections (client); + for (i = 0; i < arr->len; i++) { + NMRemoteConnection *c = NM_REMOTE_CONNECTION (arr->pdata[i]); - return nm_remote_settings_get_connection_by_uuid (NM_CLIENT_GET_PRIVATE (client)->settings, uuid); + if (nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (c)))) + return c; + } + return NULL; } -typedef struct { - NMRemoteConnection *connection; - GVariant *results; -} AddConnection2CbData; +/*****************************************************************************/ static void -add_connection2_cb_data_destroy (gpointer user_data) +_add_connection_cb (GObject *source, + GAsyncResult *result, + gboolean with_extra_arg, + gpointer user_data) { - AddConnection2CbData *data = user_data; + gs_unref_variant GVariant *ret = NULL; + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *v_result = NULL; + const char *v_path; + GError *error = NULL; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled (error, FALSE)) + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + return; + } + + if (with_extra_arg) { + g_variant_get (ret, + "(&o@a{sv})", + &v_path, + &v_result); + } else { + g_variant_get (ret, + "(&o)", + &v_path); + } - g_object_unref (data->connection); - nm_g_variant_unref (data->results); - nm_g_slice_free (data); + _request_wait_start (g_steal_pointer (&task), + "AddConnection", + NM_TYPE_REMOTE_CONNECTION, + v_path, + g_steal_pointer (&v_result)); } static void -add_connection2_cb (NMRemoteSettings *self, - NMRemoteConnection *connection, - GVariant *results, - GError *error, - gpointer user_data) +_add_connection_cb_without_extra_result (GObject *object, GAsyncResult *result, gpointer user_data) { - gs_unref_object GSimpleAsyncResult *simple = user_data; - - if (error) { - g_simple_async_result_take_error (simple, - g_error_new_literal (error->domain, - error->code, - error->message)); - } else if (g_simple_async_result_get_source_tag (simple) == nm_client_add_connection_async) { - g_simple_async_result_set_op_res_gpointer (simple, - g_object_ref (connection), - g_object_unref); - } else { - AddConnection2CbData *data; + _add_connection_cb (object, result, FALSE, user_data); +} - nm_assert (g_simple_async_result_get_source_tag (simple) == nm_client_add_connection2); +static void +_add_connection_cb_with_extra_result (GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_connection_cb (object, result, TRUE, user_data); +} - data = g_slice_new (AddConnection2CbData); - *data = (AddConnection2CbData) { - .connection = g_object_ref (connection), - .results = nm_g_variant_ref (results), - }; - g_simple_async_result_set_op_res_gpointer (simple, - data, - add_connection2_cb_data_destroy); - } +static void +_add_connection_call (NMClient *self, + gpointer source_tag, + gboolean ignore_out_result, + GVariant *settings, + NMSettingsAddConnection2Flags flags, + GVariant *args, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_CLIENT (self)); + g_return_if_fail (!settings || g_variant_is_of_type (settings, G_VARIANT_TYPE ("a{sa{sv}}"))); + g_return_if_fail (!args || g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))); - g_simple_async_result_complete (simple); + NML_NMCLIENT_LOG_D (self, "AddConnection() started..."); + + if (!settings) + settings = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0); + + /* Although AddConnection2() being capable to handle also AddConnection() and + * AddConnectionUnsaved() variants, we prefer to use the old D-Bus methods when + * they are sufficient. The reason is that libnm should avoid hard dependencies + * on 1.20 API whenever possible. */ + if ( ignore_out_result + && flags == NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK) { + _nm_client_dbus_call (self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnection", + g_variant_new ("(@a{sa{sv}})", settings), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_without_extra_result); + } else if ( ignore_out_result + && flags == NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY) { + _nm_client_dbus_call (self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnectionUnsaved", + g_variant_new ("(@a{sa{sv}})", settings), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_without_extra_result); + } else { + _nm_client_dbus_call (self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnection2", + g_variant_new ("(@a{sa{sv}}u@a{sv})", + settings, + (guint32) flags, + args + ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)), + G_VARIANT_TYPE ("(oa{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_with_extra_result); + } } /** @@ -1715,32 +5520,19 @@ nm_client_add_connection_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - - g_return_if_fail (NM_IS_CLIENT (client)); g_return_if_fail (NM_IS_CONNECTION (connection)); - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_add_connection_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - - nm_remote_settings_add_connection2 (NM_CLIENT_GET_PRIVATE (client)->settings, - nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL), - save_to_disk - ? NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK - : NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY, - NULL, - TRUE, - cancellable, - add_connection2_cb, - simple); + _add_connection_call (client, + nm_client_add_connection_async, + TRUE, + nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL), + save_to_disk + ? NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + : NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY, + NULL, + cancellable, + callback, + user_data); } /** @@ -1759,15 +5551,11 @@ nm_client_add_connection_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (client), nm_client_add_connection_async), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); + return NM_REMOTE_CONNECTION (_request_wait_finish (client, + result, + nm_client_add_connection_async, + NULL, + error)); } /** @@ -1802,33 +5590,15 @@ nm_client_add_connection2 (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - - g_return_if_fail (NM_IS_CLIENT (client)); - g_return_if_fail (g_variant_is_of_type (settings, G_VARIANT_TYPE ("a{sa{sv}}"))); - g_return_if_fail (!args || g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), - callback, - user_data, - nm_client_add_connection2); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - - nm_remote_settings_add_connection2 (NM_CLIENT_GET_PRIVATE (client)->settings, - settings, - flags, - args, - ignore_out_result, - cancellable, - add_connection2_cb, - simple); + _add_connection_call (client, + nm_client_add_connection2, + ignore_out_result, + settings, + flags, + args, + cancellable, + callback, + user_data); } /** @@ -1852,23 +5622,15 @@ nm_client_add_connection2_finish (NMClient *client, GVariant **out_result, GError **error) { - GSimpleAsyncResult *simple; - AddConnection2CbData *data; - - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (client), nm_client_add_connection2), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) { - NM_SET_OUT (out_result, NULL); - return NULL; - } - - data = g_simple_async_result_get_op_res_gpointer (simple); - NM_SET_OUT (out_result, g_variant_ref (data->results)); - return g_object_ref (data->connection); + return NM_REMOTE_CONNECTION (_request_wait_finish (client, + result, + nm_client_add_connection2, + out_result, + error)); } +/*****************************************************************************/ + /** * nm_client_load_connections: * @client: the %NMClient @@ -1890,8 +5652,18 @@ nm_client_add_connection2_finish (NMClient *client, * then @failures will be set to a %NULL-terminated array of the * filenames that failed to load. * - * Returns: %TRUE if NetworkManager at least tried to load @filenames, - * %FALSE if an error occurred (eg, permission denied). + * Returns: %TRUE on success. + * + * Warning: before libnm 1.22, the boolean return value was inconsistent. + * That is made worse, because when running against certain server versions + * before 1.20, the server would return wrong values for success/failure. + * This means, if you use this function in libnm before 1.22, you are advised + * to ignore the boolean return value and only look at @failures and @error. + * With libnm >= 1.22, the boolean return value corresponds to whether @error was + * set. Note that even in the success case, you might have individual @failures. + * With 1.22, the return value is consistent with nm_client_load_connections_finish(). + * + * Deprecated: 1.22, use nm_client_load_connections_async() or GDBusConnection **/ gboolean nm_client_load_connections (NMClient *client, @@ -1900,32 +5672,34 @@ nm_client_load_connections (NMClient *client, GCancellable *cancellable, GError **error) { - g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (filenames != NULL, FALSE); + gs_unref_variant GVariant *ret = NULL; - if (!_nm_client_check_nm_running (client, error)) + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); + + ret = _nm_client_dbus_call_sync (client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "LoadConnections", + g_variant_new ("(^as)", + filenames ?: NM_PTRARRAY_EMPTY (char *)), + G_VARIANT_TYPE ("(bas)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) { + *failures = NULL; return FALSE; + } - return nm_remote_settings_load_connections (NM_CLIENT_GET_PRIVATE (client)->settings, - filenames, failures, - cancellable, error); -} - -static void -load_connections_cb (GObject *object, GAsyncResult *result, gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - char **failures = NULL; - - if (nm_remote_settings_load_connections_finish (NM_REMOTE_SETTINGS (object), - &failures, result, &error)) - g_simple_async_result_set_op_res_gpointer (simple, failures, (GDestroyNotify) g_strfreev); - else - g_simple_async_result_take_error (simple, error); + g_variant_get (ret, + "(b^as)", + NULL, + &failures); - g_simple_async_result_complete (simple); - g_object_unref (simple); + return TRUE; } /** @@ -1948,24 +5722,24 @@ nm_client_load_connections_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); - g_return_if_fail (filenames != NULL); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_load_connections_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_remote_settings_load_connections_async (NM_CLIENT_GET_PRIVATE (client)->settings, - filenames, - cancellable, load_connections_cb, simple); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + _nm_client_dbus_call (client, + client, + nm_client_load_connections_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "LoadConnections", + g_variant_new ("(^as)", + filenames ?: NM_PTRARRAY_EMPTY (char *)), + G_VARIANT_TYPE ("(bas)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); } /** @@ -1980,8 +5754,8 @@ nm_client_load_connections_async (NMClient *client, * See nm_client_load_connections() for more details. * - * Returns: %TRUE if NetworkManager at least tried to load @filenames, - * %FALSE if an error occurred (eg, permission denied). + * Returns: %TRUE on success. + * Note that even in the success case, you might have individual @failures. **/ gboolean nm_client_load_connections_finish (NMClient *client, @@ -1989,19 +5763,23 @@ nm_client_load_connections_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; + gs_unref_variant GVariant *ret = NULL; g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_load_connections_async), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) + ret = g_task_propagate_pointer (G_TASK (result), error); + if (!ret) { + *failures = NULL; return FALSE; - else { - if (failures) - *failures = g_strdupv (g_simple_async_result_get_op_res_gpointer (simple)); - return TRUE; } + + g_variant_get (ret, + "(b^as)", + NULL, + &failures); + + return TRUE; } /** @@ -2015,35 +5793,34 @@ nm_client_load_connections_finish (NMClient *client, * the in-memory state matches the on-disk state. * * Return value: %TRUE on success, %FALSE on failure + * + * Deprecated: 1.22, use nm_client_reload_connections_async() or GDBusConnection **/ gboolean nm_client_reload_connections (NMClient *client, GCancellable *cancellable, GError **error) { - g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + gs_unref_variant GVariant *ret = NULL; - if (!_nm_client_check_nm_running (client, error)) + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); + + ret = _nm_client_dbus_call_sync (client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "ReloadConnections", + g_variant_new ("()"), + G_VARIANT_TYPE ("(b)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) return FALSE; - return nm_remote_settings_reload_connections (NM_CLIENT_GET_PRIVATE (client)->settings, - cancellable, error); -} - -static void -reload_connections_cb (GObject *object, GAsyncResult *result, gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (nm_remote_settings_reload_connections_finish (NM_REMOTE_SETTINGS (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); + return TRUE; } /** @@ -2063,22 +5840,23 @@ nm_client_reload_connections_async (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); - - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_reload_connections_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_remote_settings_reload_connections_async (NM_CLIENT_GET_PRIVATE (client)->settings, - cancellable, reload_connections_cb, simple); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + _nm_client_dbus_call (client, + client, + nm_client_reload_connections_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "ReloadConnections", + g_variant_new ("()"), + G_VARIANT_TYPE ("(b)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); } /** @@ -2096,16 +5874,16 @@ nm_client_reload_connections_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; + gs_unref_variant GVariant *ret = NULL; g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_reload_connections_async), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) + ret = g_task_propagate_pointer (G_TASK (result), error); + if (!ret) return FALSE; - else - return g_simple_async_result_get_op_res_gboolean (simple); + + return TRUE; } /*****************************************************************************/ @@ -2124,15 +5902,9 @@ nm_client_reload_connections_finish (NMClient *client, const char * nm_client_get_dns_mode (NMClient *client) { - NMClientPrivate *priv; - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - priv = NM_CLIENT_GET_PRIVATE (client); - if (priv->dns_manager) - return nm_dns_manager_get_mode (priv->dns_manager); - else - return NULL; + return NM_CLIENT_GET_PRIVATE (client)->dns_manager.mode; } /** @@ -2149,15 +5921,9 @@ nm_client_get_dns_mode (NMClient *client) const char * nm_client_get_dns_rc_manager (NMClient *client) { - NMClientPrivate *priv; - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - priv = NM_CLIENT_GET_PRIVATE (client); - if (priv->dns_manager) - return nm_dns_manager_get_rc_manager (priv->dns_manager); - else - return NULL; + return NM_CLIENT_GET_PRIVATE (client)->dns_manager.rc_manager; } /** @@ -2176,221 +5942,89 @@ nm_client_get_dns_rc_manager (NMClient *client) const GPtrArray * nm_client_get_dns_configuration (NMClient *client) { - NMClientPrivate *priv; - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - priv = NM_CLIENT_GET_PRIVATE (client); - - if (priv->dns_manager) - return nm_dns_manager_get_configuration (priv->dns_manager); - else - return NULL; -} - -/*****************************************************************************/ - -/** - * nm_client_new: - * @cancellable: a #GCancellable, or %NULL - * @error: location for a #GError, or %NULL - * - * Creates a new #NMClient. - * - * Note that this will do blocking D-Bus calls to initialize the - * client. You can use nm_client_new_async() if you want to avoid - * that. - * - * Returns: a new #NMClient or NULL on an error - **/ -NMClient * -nm_client_new (GCancellable *cancellable, - GError **error) -{ - return g_initable_new (NM_TYPE_CLIENT, cancellable, error, - NULL); -} - -static void -client_inited (GObject *source, GAsyncResult *result, gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (!g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, &error)) - g_simple_async_result_take_error (simple, error); - else - g_simple_async_result_set_op_res_gpointer (simple, source, g_object_unref); - g_simple_async_result_complete (simple); - g_object_unref (simple); -} - -/** - * nm_client_new_async: - * @cancellable: a #GCancellable, or %NULL - * @callback: callback to call when the client is created - * @user_data: data for @callback - * - * Creates a new #NMClient and begins asynchronously initializing it. - * @callback will be called when it is done; use - * nm_client_new_finish() to get the result. Note that on an error, - * the callback can be invoked with two first parameters as NULL. - **/ -void -nm_client_new_async (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (NULL, callback, user_data, nm_client_new_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - - g_async_initable_new_async (NM_TYPE_CLIENT, G_PRIORITY_DEFAULT, - cancellable, client_inited, simple, - NULL); -} - -/** - * nm_client_new_finish: - * @result: a #GAsyncResult - * @error: location for a #GError, or %NULL - * - * Gets the result of an nm_client_new_async() call. - * - * Returns: a new #NMClient, or %NULL on error - **/ -NMClient * -nm_client_new_finish (GAsyncResult *result, GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, nm_client_new_async), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - else - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); -} - -static void -subobject_notify (GObject *object, - GParamSpec *pspec, - gpointer client) -{ - if (!g_str_has_suffix (pspec->name, "-internal")) - g_object_notify (client, pspec->name); -} -static void -manager_device_added (NMManager *manager, - NMDevice *device, - gpointer client) -{ - g_signal_emit (client, signals[DEVICE_ADDED], 0, device); + return NM_CLIENT_GET_PRIVATE (client)->dns_manager.configuration; } -static void -manager_device_removed (NMManager *manager, - NMDevice *device, - gpointer client) +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_dns_manager_configuration (NMClient *self, + NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value) { - g_signal_emit (client, signals[DEVICE_REMOVED], 0, device); -} - -static void -manager_any_device_added (NMManager *manager, - NMDevice *device, - gpointer client) -{ - g_signal_emit (client, signals[ANY_DEVICE_ADDED], 0, device); -} - -static void -manager_any_device_removed (NMManager *manager, - NMDevice *device, - gpointer client) -{ - g_signal_emit (client, signals[ANY_DEVICE_REMOVED], 0, device); -} - -static void -manager_permission_changed (NMManager *manager, - NMClientPermission permission, - NMClientPermissionResult result, - gpointer client) -{ - g_signal_emit (client, signals[PERMISSION_CHANGED], 0, permission, result); -} - -static void -settings_connection_added (NMRemoteSettings *manager, - NMRemoteConnection *connection, - gpointer client) -{ - g_signal_emit (client, signals[CONNECTION_ADDED], 0, connection); -} -static void -settings_connection_removed (NMRemoteSettings *manager, - NMRemoteConnection *connection, - gpointer client) -{ - g_signal_emit (client, signals[CONNECTION_REMOVED], 0, connection); -} + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + gs_unref_ptrarray GPtrArray *configuration_old = NULL; + gs_unref_ptrarray GPtrArray *configuration_new = NULL; + + nm_assert (G_OBJECT (self) == dbobj->nmobj); + + if (value) { + GVariant *entry_var_tmp; + GVariantIter iter; + GPtrArray *array; + + configuration_new = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_dns_entry_unref); + + g_variant_iter_init (&iter, value); + while (g_variant_iter_next (&iter, "@a{sv}", &entry_var_tmp)) { + gs_unref_variant GVariant *entry_var = entry_var_tmp; + nm_auto_free_variant_iter GVariantIter *iterp_nameservers = NULL; + nm_auto_free_variant_iter GVariantIter *iterp_domains = NULL; + gs_free char **nameservers = NULL; + gs_free char **domains = NULL; + gboolean vpn = FALSE; + NMDnsEntry *entry; + char *interface = NULL; + char *str; + gint32 priority = 0; + + if ( !g_variant_lookup (entry_var, "nameservers", "as", &iterp_nameservers) + || !g_variant_lookup (entry_var, "priority", "i", &priority)) { + g_warning ("Ignoring invalid DNS configuration"); + continue; + } -static void -manager_active_connection_added (NMManager *manager, - NMActiveConnection *active_connection, - gpointer client) -{ - g_signal_emit (client, signals[ACTIVE_CONNECTION_ADDED], 0, active_connection); -} + array = g_ptr_array_new (); + while (g_variant_iter_next (iterp_nameservers, "&s", &str)) + g_ptr_array_add (array, str); + g_ptr_array_add (array, NULL); + nameservers = (char **) g_ptr_array_free (array, FALSE); + + if (g_variant_lookup (entry_var, "domains", "as", &iterp_domains)) { + array = g_ptr_array_new (); + while (g_variant_iter_next (iterp_domains, "&s", &str)) + g_ptr_array_add (array, str); + g_ptr_array_add (array, NULL); + domains = (char **) g_ptr_array_free (array, FALSE); + } -static void -manager_active_connection_removed (NMManager *manager, - NMActiveConnection *active_connection, - gpointer client) -{ - g_signal_emit (client, signals[ACTIVE_CONNECTION_REMOVED], 0, active_connection); -} + g_variant_lookup (entry_var, "interface", "&s", &interface); + g_variant_lookup (entry_var, "vpn", "b", &vpn); -static void -dns_notify (GObject *object, - GParamSpec *pspec, - gpointer client) -{ - char pname[128]; + entry = nm_dns_entry_new (interface, + (const char *const*) nameservers, + (const char *const*) domains, + priority, + vpn); + if (!entry) { + g_warning ("Ignoring invalid DNS entry"); + continue; + } - if (NM_IN_STRSET (pspec->name, - NM_DNS_MANAGER_MODE, - NM_DNS_MANAGER_RC_MANAGER, - NM_DNS_MANAGER_CONFIGURATION)) { - nm_sprintf_buf (pname, "dns-%s", pspec->name); - g_object_notify (client, pname); + g_ptr_array_add (configuration_new, entry); + } } -} -static void -checkpoint_create_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - NMCheckpoint *checkpoint; - GError *error = NULL; - - checkpoint = nm_manager_checkpoint_create_finish (NM_MANAGER (object), result, &error); - if (checkpoint) - g_simple_async_result_set_op_res_gpointer (simple, checkpoint, g_object_unref); - else - g_simple_async_result_take_error (simple, error); + configuration_old = priv->dns_manager.configuration; + priv->dns_manager.configuration = g_steal_pointer (&configuration_new); - g_simple_async_result_complete (simple); - g_object_unref (simple); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; } +/*****************************************************************************/ + /** * nm_client_get_checkpoints: * @client: a #NMClient @@ -2408,10 +6042,36 @@ nm_client_get_checkpoints (NMClient *client) { g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - if (!nm_client_get_nm_running (client)) - return ∅ + return nml_dbus_property_ao_get_objs_as_ptrarray (&NM_CLIENT_GET_PRIVATE (client)->nm.property_ao[PROPERTY_AO_IDX_CHECKPOINTS]); +} - return nm_manager_get_checkpoints (NM_CLIENT_GET_PRIVATE (client)->manager); +static void +checkpoint_create_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *ret = NULL; + const char *v_checkpoint_path; + GError *error = NULL; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled (error, FALSE)) + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + return; + } + + g_variant_get (ret, + "(&o)", + &v_checkpoint_path); + + _request_wait_start (g_steal_pointer (&task), + "CheckpointCreate", + NM_TYPE_CHECKPOINT, + v_checkpoint_path, + NULL); } /** @@ -2441,23 +6101,36 @@ nm_client_checkpoint_create (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; + gs_free const char **paths = NULL; + guint i; g_return_if_fail (NM_IS_CLIENT (client)); - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; + if ( devices + && devices->len > 0) { + paths = g_new (const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) + paths[i] = nm_object_get_path (NM_OBJECT (devices->pdata[i])); + paths[i] = NULL; } - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_checkpoint_create); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_checkpoint_create (NM_CLIENT_GET_PRIVATE (client)->manager, - devices, rollback_timeout, flags, - cancellable, checkpoint_create_cb, simple); + _nm_client_dbus_call (client, + client, + nm_client_checkpoint_create, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointCreate", + g_variant_new ("(^aouu)", + paths ?: NM_PTRARRAY_EMPTY (const char *), + rollback_timeout, + flags), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + checkpoint_create_cb); } /** @@ -2478,33 +6151,11 @@ nm_client_checkpoint_create_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - - g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - else - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); -} - -static void -checkpoint_destroy_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (nm_manager_checkpoint_destroy_finish (NM_MANAGER (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); + return NM_CHECKPOINT (_request_wait_finish (client, + result, + nm_client_checkpoint_create, + NULL, + error)); } /** @@ -2526,24 +6177,23 @@ nm_client_checkpoint_destroy (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/'); - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_checkpoint_destroy); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_checkpoint_destroy (NM_CLIENT_GET_PRIVATE (client)->manager, - checkpoint_path, - cancellable, checkpoint_destroy_cb, simple); + _nm_client_dbus_call (client, + client, + nm_client_checkpoint_destroy, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointDestroy", + g_variant_new ("(o)", checkpoint_path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); } /** @@ -2564,35 +6214,10 @@ nm_client_checkpoint_destroy_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_checkpoint_destroy), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - else - return g_simple_async_result_get_op_res_gboolean (simple); -} - -static void -checkpoint_rollback_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - GHashTable *hash; - GError *error = NULL; - - hash = nm_manager_checkpoint_rollback_finish (NM_MANAGER (object), result, &error); - if (hash) - g_simple_async_result_set_op_res_gpointer (simple, hash, (GDestroyNotify) g_hash_table_unref); - else - g_simple_async_result_take_error (simple, error); - - g_simple_async_result_complete (simple); - g_object_unref (simple); + return g_task_propagate_boolean (G_TASK (result), error); } /** @@ -2614,24 +6239,23 @@ nm_client_checkpoint_rollback (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/'); - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_checkpoint_rollback); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_checkpoint_rollback (NM_CLIENT_GET_PRIVATE (client)->manager, - checkpoint_path, - cancellable, checkpoint_rollback_cb, simple); + _nm_client_dbus_call (client, + client, + nm_client_checkpoint_rollback, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointRollback", + g_variant_new ("(o)", checkpoint_path), + G_VARIANT_TYPE ("(a{su})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); } /** @@ -2653,35 +6277,31 @@ nm_client_checkpoint_rollback_finish (NMClient *client, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_result = NULL; + GVariantIter iter; GHashTable *hash; + const char *path; + guint32 r; g_return_val_if_fail (NM_IS_CLIENT (client), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_checkpoint_rollback), NULL); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) + ret = g_task_propagate_pointer (G_TASK (result), error); + if (!ret) return NULL; - else { - hash = g_simple_async_result_get_op_res_gpointer (simple); - return g_hash_table_ref (hash); - } -} -static void -checkpoint_adjust_rollback_timeout_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - gs_unref_object GSimpleAsyncResult *simple = user_data; - GError *error = NULL; + g_variant_get (ret, + "(@a{su})", + &v_result); - if (nm_manager_checkpoint_adjust_rollback_timeout_finish (NM_MANAGER (object), result, &error)) - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - else - g_simple_async_result_take_error (simple, error); + hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - g_simple_async_result_complete (simple); + g_variant_iter_init (&iter, v_result); + while (g_variant_iter_next (&iter, "{&su}", &path, &r)) + g_hash_table_insert (hash, g_strdup (path), GUINT_TO_POINTER (r)); + + return hash; } /** @@ -2707,24 +6327,25 @@ nm_client_checkpoint_adjust_rollback_timeout (NMClient *client, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; - GError *error = NULL; - g_return_if_fail (NM_IS_CLIENT (client)); g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/'); - if (!_nm_client_check_nm_running (client, &error)) { - g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error); - return; - } - - simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, - nm_client_checkpoint_rollback); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - nm_manager_checkpoint_adjust_rollback_timeout (NM_CLIENT_GET_PRIVATE (client)->manager, - checkpoint_path, add_timeout, - cancellable, checkpoint_adjust_rollback_timeout_cb, simple); + _nm_client_dbus_call (client, + client, + nm_client_checkpoint_adjust_rollback_timeout, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointAdjustRollbackTimeout", + g_variant_new ("(ou)", + checkpoint_path, + add_timeout), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); } /** @@ -2745,654 +6366,559 @@ nm_client_checkpoint_adjust_rollback_timeout_finish (NMClient *client, GError **error) { g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_checkpoint_adjust_rollback_timeout), FALSE); - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), - error); + return g_task_propagate_boolean (G_TASK (result), error); } -/****************************************************************/ -/* Object Initialization */ -/****************************************************************/ - -static GType -proxy_type (GDBusObjectManagerClient *manager, - const char *object_path, - const char *interface_name, - gpointer user_data) -{ - /* ObjectManager asks us for an object proxy. Unfortunately, we can't - * decide that by interface name and GDBusObjectManager doesn't allow - * us to look at the known interface list. Thus we need to create a - * generic GDBusObject and only couple a NMObject subclass later. */ - if (!interface_name) - return G_TYPE_DBUS_OBJECT_PROXY; - - /* An interface proxy */ - if (strcmp (interface_name, NM_DBUS_INTERFACE) == 0) - return NMDBUS_TYPE_MANAGER_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_DEVICE_WIRELESS) == 0) - return NMDBUS_TYPE_DEVICE_WIFI_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_DEVICE_WIFI_P2P) == 0) - return NMDBUS_TYPE_DEVICE_WIFI_P2P_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_DEVICE) == 0) - return NMDBUS_TYPE_DEVICE_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_SETTINGS_CONNECTION) == 0) - return NMDBUS_TYPE_SETTINGS_CONNECTION_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_SETTINGS) == 0) - return NMDBUS_TYPE_SETTINGS_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_DNS_MANAGER) == 0) - return NMDBUS_TYPE_DNS_MANAGER_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_VPN_CONNECTION) == 0) - return NMDBUS_TYPE_VPN_CONNECTION_PROXY; - else if (strcmp (interface_name, NM_DBUS_INTERFACE_ACTIVE_CONNECTION) == 0) - return NMDBUS_TYPE_ACTIVE_CONNECTION_PROXY; - - /* Use a generic D-Bus Proxy whenever we can. The typed GDBusProxy - * subclasses actually use quite some memory, so they're better avoided. */ - return G_TYPE_DBUS_PROXY; -} - -static NMObject * -obj_nm_for_gdbus_object (NMClient *self, GDBusObject *object, GDBusObjectManager *object_manager) +/** + * nm_client_reload: + * @client: the %NMClient + * @flags: flags indicating what to reload. + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Reload NetworkManager's configuration and perform certain updates, like + * flushing caches or rewriting external state to disk. This is similar to + * sending SIGHUP to NetworkManager but it allows for more fine-grained control + * over what to reload (see @flags). It also allows non-root access via + * PolicyKit and contrary to signals it is synchronous. + * + * Since: 1.22 + **/ +void +nm_client_reload (NMClient *client, + NMManagerReloadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - NMClientPrivate *priv; - GList *interfaces; - GList *l; - GType type = G_TYPE_INVALID; - NMObject *obj_nm; - - g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (object), NULL); - - interfaces = g_dbus_object_get_interfaces (object); - for (l = interfaces; l; l = l->next) { - GDBusProxy *proxy = G_DBUS_PROXY (l->data); - const char *ifname = g_dbus_proxy_get_interface_name (proxy); - - /* This is a performance/scalability hack. It makes sense to call it - * from here, since this is in the common object creation path. */ - _nm_dbus_proxy_replace_match (proxy); - - if (strcmp (ifname, NM_DBUS_INTERFACE) == 0) - type = NM_TYPE_MANAGER; - else if (strcmp (ifname, NM_DBUS_INTERFACE_ACCESS_POINT) == 0) - type = NM_TYPE_ACCESS_POINT; - else if (strcmp (ifname, NM_DBUS_INTERFACE_ACTIVE_CONNECTION) == 0 && type != NM_TYPE_VPN_CONNECTION) - type = NM_TYPE_ACTIVE_CONNECTION; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_6LOWPAN) == 0) - type = NM_TYPE_DEVICE_6LOWPAN; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_ADSL) == 0) - type = NM_TYPE_DEVICE_ADSL; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_BOND) == 0) - type = NM_TYPE_DEVICE_BOND; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_BRIDGE) == 0) - type = NM_TYPE_DEVICE_BRIDGE; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_BLUETOOTH) == 0) - type = NM_TYPE_DEVICE_BT; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_DUMMY) == 0) - type = NM_TYPE_DEVICE_DUMMY; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_WIRED) == 0) - type = NM_TYPE_DEVICE_ETHERNET; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_GENERIC) == 0) - type = NM_TYPE_DEVICE_GENERIC; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_INFINIBAND) == 0) - type = NM_TYPE_DEVICE_INFINIBAND; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL) == 0) - type = NM_TYPE_DEVICE_IP_TUNNEL; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_MACSEC) == 0) - type = NM_TYPE_DEVICE_MACSEC; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_MACVLAN) == 0) - type = NM_TYPE_DEVICE_MACVLAN; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_MODEM) == 0) - type = NM_TYPE_DEVICE_MODEM; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OLPC_MESH) == 0) - type = NM_TYPE_DEVICE_OLPC_MESH; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE) == 0) - type = NM_TYPE_DEVICE_OVS_INTERFACE; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_PORT) == 0) - type = NM_TYPE_DEVICE_OVS_PORT; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE) == 0) - type = NM_TYPE_DEVICE_OVS_BRIDGE; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_WIFI_P2P) == 0) - type = NM_TYPE_DEVICE_WIFI_P2P; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_PPP) == 0) - type = NM_TYPE_DEVICE_PPP; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_TEAM) == 0) - type = NM_TYPE_DEVICE_TEAM; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_TUN) == 0) - type = NM_TYPE_DEVICE_TUN; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_VLAN) == 0) - type = NM_TYPE_DEVICE_VLAN; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_WPAN) == 0) - type = NM_TYPE_DEVICE_WPAN; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_VXLAN) == 0) - type = NM_TYPE_DEVICE_VXLAN; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_WIRELESS) == 0) - type = NM_TYPE_DEVICE_WIFI; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_WIMAX) == 0) - type = NM_TYPE_DEVICE_WIMAX; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_WIREGUARD) == 0) - type = NM_TYPE_DEVICE_WIREGUARD; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DHCP4_CONFIG) == 0) - type = NM_TYPE_DHCP4_CONFIG; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DHCP6_CONFIG) == 0) - type = NM_TYPE_DHCP6_CONFIG; - else if (strcmp (ifname, NM_DBUS_INTERFACE_IP4_CONFIG) == 0) - type = NM_TYPE_IP4_CONFIG; - else if (strcmp (ifname, NM_DBUS_INTERFACE_IP6_CONFIG) == 0) - type = NM_TYPE_IP6_CONFIG; - else if (strcmp (ifname, NM_DBUS_INTERFACE_WIFI_P2P_PEER) == 0) - type = NM_TYPE_WIFI_P2P_PEER; - else if (strcmp (ifname, NM_DBUS_INTERFACE_SETTINGS_CONNECTION) == 0) - type = NM_TYPE_REMOTE_CONNECTION; - else if (strcmp (ifname, NM_DBUS_INTERFACE_SETTINGS) == 0) - type = NM_TYPE_REMOTE_SETTINGS; - else if (strcmp (ifname, NM_DBUS_INTERFACE_DNS_MANAGER) == 0) - type = NM_TYPE_DNS_MANAGER; - else if (strcmp (ifname, NM_DBUS_INTERFACE_VPN_CONNECTION) == 0) - type = NM_TYPE_VPN_CONNECTION; - else if (strcmp (ifname, NM_DBUS_INTERFACE_WIMAX_NSP) == 0) - type = NM_TYPE_WIMAX_NSP; - else if (strcmp (ifname, NM_DBUS_INTERFACE_CHECKPOINT) == 0) - type = NM_TYPE_CHECKPOINT; - - if (type != G_TYPE_INVALID) - break; - } - - g_list_free_full (interfaces, g_object_unref); - if (type == G_TYPE_INVALID) - return NULL; + g_return_if_fail (NM_IS_CLIENT (client)); - obj_nm = g_object_new (type, - NM_OBJECT_DBUS_OBJECT, object, - NM_OBJECT_DBUS_OBJECT_MANAGER, object_manager, - NULL); - if (NM_IS_DEVICE (obj_nm)) { - priv = NM_CLIENT_GET_PRIVATE (self); - if (G_UNLIKELY (!priv->udev_inited)) { - priv->udev_inited = TRUE; - /* for testing, we don't want to use udev in libnm. */ - if (!nm_streq0 (g_getenv ("LIBNM_USE_NO_UDEV"), "1")) - priv->udev = udev_new (); - } - if (priv->udev) - _nm_device_set_udev (NM_DEVICE (obj_nm), priv->udev); - } - g_object_set_qdata_full (G_OBJECT (object), _nm_object_obj_nm_quark (), - obj_nm, g_object_unref); - return obj_nm; + _nm_client_dbus_call (client, + client, + nm_client_reload, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "Reload", + g_variant_new ("(u)", (guint32) flags), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); } -static void -obj_nm_inited (GObject *object, GAsyncResult *result, gpointer user_data) +/** + * nm_client_reload_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_reload(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.22 + **/ +gboolean +nm_client_reload_finish (NMClient *client, + GAsyncResult *result, + GError **error) { - if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, NULL)) { - /* This is a can-not-happen situation, the NMObject subclasses are not - * supposed to fail initialization. */ - g_warn_if_reached (); - } + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, client, nm_client_reload), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); } +/*****************************************************************************/ + static void -object_added (GDBusObjectManager *object_manager, GDBusObject *object, gpointer user_data) +_init_fetch_all (NMClient *self) { - NMClient *client = user_data; - NMObject *obj_nm; - - obj_nm = obj_nm_for_gdbus_object (client, object, object_manager); - if (obj_nm) { - g_async_initable_init_async (G_ASYNC_INITABLE (obj_nm), - G_PRIORITY_DEFAULT, NULL, - obj_nm_inited, NULL); - } + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->dbus_context); + + NML_NMCLIENT_LOG_D (self, "fetch all"); + + nm_assert (!priv->get_managed_objects_cancellable); + + priv->get_managed_objects_cancellable = g_cancellable_new (); + + priv->dbsid_nm_object_manager = nm_dbus_connection_signal_subscribe_object_manager (priv->dbus_connection, + priv->name_owner, + "/org/freedesktop", + _dbus_managed_objects_changed_cb, + self, + NULL); + + priv->dbsid_dbus_properties_properties_changed = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection, + priv->name_owner, + NULL, + NULL, + _dbus_properties_changed_cb, + self, + NULL); + + priv->dbsid_nm_settings_connection_updated = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Updated", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_settings_updated_cb, + self, + NULL); + + priv->dbsid_nm_connection_active_state_changed = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + "StateChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_connection_active_state_changed_cb, + self, + NULL); + + priv->dbsid_nm_vpn_connection_state_changed = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_VPN_CONNECTION, + "VpnStateChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_vpn_connection_state_changed_cb, + self, + NULL); + + priv->dbsid_nm_check_permissions = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE, + "CheckPermissions", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_check_permissions_cb, + self, + NULL); + + g_dbus_connection_call (priv->dbus_connection, + priv->name_owner, + "/org/freedesktop", + DBUS_INTERFACE_OBJECT_MANAGER, + "GetManagedObjects", + NULL, + G_VARIANT_TYPE ("(a{oa{sa{sv}}})"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + priv->get_managed_objects_cancellable, + _dbus_get_managed_objects_cb, + nm_utils_user_data_pack (self, g_object_ref (priv->context_busy_watcher))); + + _dbus_check_permissions_start (self); } static void -object_removed (GDBusObjectManager *object_manager, GDBusObject *object, gpointer user_data) +_init_release_all (NMClient *self) { - g_object_set_qdata (G_OBJECT (object), _nm_object_obj_nm_quark (), NULL); -} - -static gboolean -objects_created (NMClient *client, GDBusObjectManager *object_manager, GError **error) -{ - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); - gs_unref_object GDBusObject *manager = NULL; - gs_unref_object GDBusObject *settings = NULL; - gs_unref_object GDBusObject *dns_manager = NULL; - NMObject *obj_nm; - GList *objects, *iter; - - /* First just ensure all the NMObjects for known GDBusObjects exist. */ - objects = g_dbus_object_manager_get_objects (object_manager); - for (iter = objects; iter; iter = iter->next) - obj_nm_for_gdbus_object (client, iter->data, object_manager); - g_list_free_full (objects, g_object_unref); - - manager = g_dbus_object_manager_get_object (object_manager, NM_DBUS_PATH); - if (!manager) { - g_set_error_literal (error, - NM_CLIENT_ERROR, - NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, - "Manager object not found"); - return FALSE; - } - - obj_nm = g_object_get_qdata (G_OBJECT (manager), _nm_object_obj_nm_quark ()); - if (!obj_nm) { - g_set_error_literal (error, - NM_CLIENT_ERROR, - NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, - "Manager object lacks the proper interface"); - return FALSE; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + CList **dbus_objects_lst_heads; + NMLDBusObject *dbobj; + int i; + + NML_NMCLIENT_LOG_D (self, "release all"); + + nm_clear_g_cancellable (&priv->permissions_cancellable); + nm_clear_g_cancellable (&priv->get_managed_objects_cancellable); + + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->dbsid_nm_object_manager); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->dbsid_dbus_properties_properties_changed); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->dbsid_nm_settings_connection_updated); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->dbsid_nm_connection_active_state_changed); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->dbsid_nm_vpn_connection_state_changed); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->dbsid_nm_check_permissions); + + if (priv->permissions) { + gs_unref_hashtable GHashTable *old_permissions = g_steal_pointer (&priv->permissions); + + _emit_permissions_changed (self, old_permissions, TRUE); } - priv->manager = NM_MANAGER (g_object_ref (obj_nm)); - - g_signal_connect (priv->manager, "notify", - G_CALLBACK (subobject_notify), client); - g_signal_connect (priv->manager, "device-added", - G_CALLBACK (manager_device_added), client); - g_signal_connect (priv->manager, "device-removed", - G_CALLBACK (manager_device_removed), client); - g_signal_connect (priv->manager, "any-device-added", - G_CALLBACK (manager_any_device_added), client); - g_signal_connect (priv->manager, "any-device-removed", - G_CALLBACK (manager_any_device_removed), client); - g_signal_connect (priv->manager, "permission-changed", - G_CALLBACK (manager_permission_changed), client); - g_signal_connect (priv->manager, "active-connection-added", - G_CALLBACK (manager_active_connection_added), client); - g_signal_connect (priv->manager, "active-connection-removed", - G_CALLBACK (manager_active_connection_removed), client); - - settings = g_dbus_object_manager_get_object (object_manager, NM_DBUS_PATH_SETTINGS); - if (!settings) { - g_set_error_literal (error, - NM_CLIENT_ERROR, - NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, - "Settings object not found"); - return FALSE; + nm_assert (c_list_is_empty (&priv->obj_changed_lst_head)); + + dbus_objects_lst_heads = ((CList *[]) { + &priv->dbus_objects_lst_head_on_dbus, + &priv->dbus_objects_lst_head_with_nmobj_not_ready, + &priv->dbus_objects_lst_head_with_nmobj_ready, + NULL, + }); + for (i = 0; dbus_objects_lst_heads[i]; i++) { + c_list_for_each_entry (dbobj, dbus_objects_lst_heads[i], dbus_objects_lst) { + NMLDBusObjIfaceData *db_iface_data; + + nm_assert (c_list_is_empty (&dbobj->obj_changed_lst)); + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) + db_iface_data->iface_removed = TRUE; + nml_dbus_object_obj_changed_link (self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + } } - obj_nm = g_object_get_qdata (G_OBJECT (settings), _nm_object_obj_nm_quark ()); - if (!obj_nm) { - g_set_error_literal (error, - NM_CLIENT_ERROR, - NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, - "Settings object lacks the proper interface"); - return FALSE; - } + _dbus_handle_changes (self, "release-all", FALSE); + + /* We require that when we remove all D-Bus interfaces, that all object will go + * away. Note that a NMLDBusObject can be alive due to a NMLDBusObjWatcher, but + * even those should be all cleaned up. */ + nm_assert (c_list_is_empty (&priv->obj_changed_lst_head)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_watched_only)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_on_dbus)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_with_nmobj_not_ready)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_with_nmobj_ready)); + nm_assert (g_hash_table_size (priv->dbus_objects) == 0); +} - priv->settings = NM_REMOTE_SETTINGS (g_object_ref (obj_nm)); +/*****************************************************************************/ - g_signal_connect (priv->settings, "notify", - G_CALLBACK (subobject_notify), client); - g_signal_connect (priv->settings, "connection-added", - G_CALLBACK (settings_connection_added), client); - g_signal_connect (priv->settings, "connection-removed", - G_CALLBACK (settings_connection_removed), client); +static void +name_owner_changed (NMClient *self, + const char *name_owner) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + gboolean changed; + gs_free char *old_name_owner_free = NULL; + const char *old_name_owner; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; - dns_manager = g_dbus_object_manager_get_object (object_manager, NM_DBUS_PATH_DNS_MANAGER); - if (dns_manager) { - obj_nm = g_object_get_qdata (G_OBJECT (dns_manager), _nm_object_obj_nm_quark ()); - if (!obj_nm) { - g_set_error_literal (error, - NM_CLIENT_ERROR, - NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, - "DNS manager object lacks the proper interface"); - return FALSE; - } - priv->dns_manager = NM_DNS_MANAGER (g_object_ref (obj_nm)); + name_owner = nm_str_not_empty (name_owner); - g_signal_connect (priv->dns_manager, "notify", - G_CALLBACK (dns_notify), client); - } + changed = !nm_streq0 (priv->name_owner, name_owner); - /* The handlers don't really use the client instance. However - * it makes it convenient to unhook them by data. */ - g_signal_connect (object_manager, "object-added", - G_CALLBACK (object_added), client); - g_signal_connect (object_manager, "object-removed", - G_CALLBACK (object_removed), client); + if ( !name_owner + && priv->main_context != priv->dbus_context) { + gs_unref_object GObject *old_context_busy_watcher = NULL; - return TRUE; -} + NML_NMCLIENT_LOG_D (self, "resync main context as we have no name owner"); -/* Synchronous initialization. */ + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->name_owner_changed_id); -static void name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data); + /* Our instance was initialized synchronously. Usually we must henceforth + * stick to a internal main context. But now we have no name-owner... + * at this point, we anyway are going to do a full resync. Swap the main + * contexts again. */ -static gboolean -_om_has_name_owner (GDBusObjectManager *object_manager) -{ - gs_free char *name_owner = NULL; + old_context_busy_watcher = g_steal_pointer (&priv->context_busy_watcher); + priv->context_busy_watcher = g_object_ref (g_object_get_qdata (old_context_busy_watcher, + nm_client_context_busy_watcher_quark ())); - nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); + g_main_context_ref (priv->main_context); + g_main_context_unref (priv->dbus_context); + priv->dbus_context = priv->main_context; - name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); - return !!name_owner; -} + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->dbus_context); -static gboolean -init_sync (GInitable *initable, GCancellable *cancellable, GError **error) -{ - NMClient *client = NM_CLIENT (initable); - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); - GList *objects, *iter; + /* we need to sync again... */ - priv->object_manager = g_dbus_object_manager_client_new_for_bus_sync (_nm_dbus_bus_type (), - G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, - "org.freedesktop.NetworkManager", - "/org/freedesktop", - proxy_type, NULL, NULL, - cancellable, error); + _assert_main_context_is_current_thread_default (self, dbus_context); - if (!priv->object_manager) - return FALSE; + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + NM_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + name_owner_get_call (self); + } else + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->dbus_context); + + if (changed) { + NML_NMCLIENT_LOG_D (self, "name owner changed: %s%s%s -> %s%s%s", + NM_PRINT_FMT_QUOTE_STRING (priv->name_owner), + NM_PRINT_FMT_QUOTE_STRING (name_owner)); + old_name_owner_free = priv->name_owner; + priv->name_owner = g_strdup (name_owner); + old_name_owner = old_name_owner_free; + } else + old_name_owner = priv->name_owner; - if (_om_has_name_owner (priv->object_manager)) { - if (!objects_created (client, priv->object_manager, error)) - return FALSE; + if (changed) + _notify (self, PROP_DBUS_NAME_OWNER); - objects = g_dbus_object_manager_get_objects (priv->object_manager); - for (iter = objects; iter; iter = iter->next) { - NMObject *obj_nm; + if ( changed + && old_name_owner) + _init_release_all (self); - obj_nm = g_object_get_qdata (iter->data, _nm_object_obj_nm_quark ()); - if (!obj_nm) - continue; + if ( changed + && priv->name_owner) + _init_fetch_all (self); - if (!g_initable_init (G_INITABLE (obj_nm), cancellable, NULL)) { - /* This is a can-not-happen situation, the NMObject subclasses are not - * supposed to fail initialization. */ - g_warn_if_reached (); - } - } - g_list_free_full (objects, g_object_unref); - } + _set_nm_running (self); - g_signal_connect (priv->object_manager, "notify::name-owner", - G_CALLBACK (name_owner_changed), client); + if (priv->init_data) { + nm_auto_pop_gmaincontext GMainContext *main_context = NULL; - return TRUE; + if (priv->main_context != priv->dbus_context) + main_context = nm_g_main_context_push_thread_default_if_necessary (priv->main_context); + _init_start_check_complete (self); + } } -/* Asynchronous initialization. */ - static void -init_async_complete (NMClientInitData *init_data) +name_owner_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { - if (init_data->pending_init > 0) - return; - g_simple_async_result_complete (init_data->result); - g_object_unref (init_data->result); - g_clear_object (&init_data->cancellable); - g_slice_free (NMClientInitData, init_data); -} + NMClient *self = user_data; + NMClientPrivate *priv; + const char *new_owner; -static void -async_inited_obj_nm (GObject *object, GAsyncResult *result, gpointer user_data) -{ - NMClientInitData *init_data = user_data; - GError *error = NULL; + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) + return; - nm_assert (init_data && init_data->pending_init > 0); + priv = NM_CLIENT_GET_PRIVATE (self); + if (priv->name_owner_get_cancellable) + return; - if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error)) - g_simple_async_result_take_error (init_data->result, error); + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &new_owner); - init_data->pending_init--; - init_async_complete (init_data); + name_owner_changed (self, new_owner); } static void -init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, - gpointer user_data); - -static void -unhook_om (NMClient *self) +name_owner_get_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) { - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - GList *objects, *iter; + NMClient *self; + NMClientPrivate *priv; + gs_unref_object GObject *context_busy_watcher = NULL; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + const char *name_owner = NULL; - if (priv->manager) { - const GPtrArray *active_connections; - const GPtrArray *devices; - int i; + nm_utils_user_data_unpack (user_data, &self, &context_busy_watcher); - active_connections = nm_manager_get_active_connections (priv->manager); - for (i = 0; i < active_connections->len; i++) - g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active_connections->pdata[i]); + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); - devices = nm_manager_get_all_devices (priv->manager); - for (i = 0; i < devices->len; i++) - g_signal_emit (self, signals[DEVICE_REMOVED], 0, devices->pdata[i]); + if ( !ret + && nm_utils_error_is_cancelled (error, FALSE)) + return; - g_signal_handlers_disconnect_by_data (priv->manager, self); - g_clear_object (&priv->manager); - g_object_notify (G_OBJECT (self), NM_CLIENT_ACTIVE_CONNECTIONS); - g_object_notify (G_OBJECT (self), NM_CLIENT_NM_RUNNING); - } - if (priv->settings) { - const GPtrArray *connections; - guint i; + priv = NM_CLIENT_GET_PRIVATE (self); - connections = nm_remote_settings_get_connections (priv->settings); - for (i = 0; i < connections->len; i++) - g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connections->pdata[i]); + g_clear_object (&priv->name_owner_get_cancellable); - g_signal_handlers_disconnect_by_data (priv->settings, self); - g_clear_object (&priv->settings); - g_object_notify (G_OBJECT (self), NM_CLIENT_CONNECTIONS); - g_object_notify (G_OBJECT (self), NM_CLIENT_HOSTNAME); - g_object_notify (G_OBJECT (self), NM_CLIENT_CAN_MODIFY); - } - if (priv->dns_manager) { - g_signal_handlers_disconnect_by_data (priv->dns_manager, self); - g_clear_object (&priv->dns_manager); - } + if (ret) + g_variant_get (ret, "(&s)", &name_owner); - objects = g_dbus_object_manager_get_objects (priv->object_manager); - for (iter = objects; iter; iter = iter->next) - g_object_set_qdata (iter->data, _nm_object_obj_nm_quark (), NULL); - g_list_free_full (objects, g_object_unref); + name_owner_changed (self, name_owner); } static void -new_object_manager (GObject *source_object, GAsyncResult *res, gpointer user_data) +name_owner_get_call (NMClient *self) { - NMClient *self = NM_CLIENT (user_data); NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - g_clear_object (&priv->new_object_manager_cancellable); - g_object_notify (G_OBJECT (user_data), NM_CLIENT_NM_RUNNING); + nm_assert (!priv->name_owner_get_cancellable); + priv->name_owner_get_cancellable = g_cancellable_new (); + + g_dbus_connection_call (priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new ("(s)", NM_DBUS_SERVICE), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + priv->name_owner_get_cancellable, + name_owner_get_cb, + nm_utils_user_data_pack (self, g_object_ref (priv->context_busy_watcher))); } +/*****************************************************************************/ + static void -got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) +_init_start_complete (NMClient *self, + GError *error_take) { - NMClientInitData *init_data = user_data; - NMClient *client; - NMClientPrivate *priv; - GList *objects, *iter; - GError *error = NULL; - GDBusObjectManager *object_manager; - - object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error); - if (object_manager == NULL) { - g_simple_async_result_take_error (init_data->result, error); - init_async_complete (init_data); - return; - } - - client = init_data->client; - priv = NM_CLIENT_GET_PRIVATE (client); - priv->object_manager = object_manager; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + InitData *init_data; - if (_om_has_name_owner (priv->object_manager)) { - if (!objects_created (client, priv->object_manager, &error)) { - g_simple_async_result_take_error (init_data->result, error); - init_async_complete (init_data); - return; - } + init_data = g_steal_pointer (&priv->init_data); - objects = g_dbus_object_manager_get_objects (priv->object_manager); - for (iter = objects; iter; iter = iter->next) { - NMObject *obj_nm; + NML_NMCLIENT_LOG_D (self, "%s init complete with %s%s%s", + init_data->is_sync ? "sync" : "async", + NM_PRINT_FMT_QUOTED (error_take, "error: ", error_take->message, "", "success")); - obj_nm = g_object_get_qdata (iter->data, _nm_object_obj_nm_quark ()); - if (!obj_nm) - continue; + nm_clear_pointer (&init_data->cancel_on_idle_source, nm_g_source_destroy_and_unref); + nm_clear_g_signal_handler (init_data->cancellable, &init_data->cancelled_id); - init_data->pending_init++; - g_async_initable_init_async (G_ASYNC_INITABLE (obj_nm), - G_PRIORITY_DEFAULT, init_data->cancellable, - async_inited_obj_nm, init_data); - } - g_list_free_full (objects, g_object_unref); + if (init_data->is_sync) { + if (error_take) + g_propagate_error (init_data->data.sync.error_location, error_take); + g_main_loop_quit (init_data->data.sync.main_loop); + } else { + if (error_take) + g_task_return_error (init_data->data.async.task, error_take); + else + g_task_return_boolean (init_data->data.async.task, TRUE); + g_object_unref (init_data->data.async.task); } - - init_async_complete (init_data); - - g_signal_connect (priv->object_manager, "notify::name-owner", - G_CALLBACK (name_owner_changed), client); + nm_g_object_unref (init_data->cancellable); + nm_g_slice_free (init_data); } static void -prepare_object_manager (NMClient *client, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +_init_start_check_complete (NMClient *self) { - NMClientInitData *init_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - init_data = g_slice_new0 (NMClientInitData); - init_data->client = client; - init_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - init_data->result = g_simple_async_result_new (G_OBJECT (client), callback, - user_data, init_async); - if (cancellable) - g_simple_async_result_set_check_cancellable (init_data->result, cancellable); - g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE); + _assert_main_context_is_current_thread_default (self, main_context); - g_dbus_object_manager_client_new_for_bus (_nm_dbus_bus_type (), - G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, - "org.freedesktop.NetworkManager", - "/org/freedesktop", - proxy_type, NULL, NULL, - init_data->cancellable, - got_object_manager, - init_data); -} + if (!priv->init_data) + return; -static void -name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data) -{ - NMClient *self = user_data; - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - GDBusObjectManager *object_manager = G_DBUS_OBJECT_MANAGER (object); + if (priv->get_managed_objects_cancellable) { + /* still initializing. Wait. */ + return; + } - nm_assert (object_manager == priv->object_manager); +#if NM_MORE_ASSERTS > 10 + { + NMLDBusObject *dbobj; - if (_om_has_name_owner (object_manager)) { - g_signal_handlers_disconnect_by_data (priv->object_manager, self); - g_clear_object (&priv->object_manager); - nm_clear_g_cancellable (&priv->new_object_manager_cancellable); - priv->new_object_manager_cancellable = g_cancellable_new (); - prepare_object_manager (self, priv->new_object_manager_cancellable, - new_object_manager, self); - } else { - g_signal_handlers_disconnect_by_func (object_manager, object_added, self); - unhook_om (self); + c_list_for_each_entry (dbobj, &priv->dbus_objects_lst_head_with_nmobj_not_ready, dbus_objects_lst) { + NML_NMCLIENT_LOG_T (self, "init-start waiting for %s", dbobj->dbus_path->str); + break; + } } +#endif + + if (!c_list_is_empty (&priv->dbus_objects_lst_head_with_nmobj_not_ready)) + return; + + _init_start_complete (self, NULL); } static void -init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, - gpointer user_data) +_init_start_cancelled_cb (GCancellable *cancellable, + gpointer user_data) { - prepare_object_manager (NM_CLIENT (initable), cancellable, callback, user_data); + NMClient *self = user_data; + GError *error; + + nm_assert (NM_IS_CLIENT (self)); + nm_assert (NM_CLIENT_GET_PRIVATE (self)->init_data); + nm_assert (NM_CLIENT_GET_PRIVATE (self)->init_data->cancellable == cancellable); + + nm_utils_error_set_cancelled (&error, FALSE, NULL); + _init_start_complete (self, error); } static gboolean -init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +_init_start_cancel_on_idle_cb (gpointer user_data) { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + NMClient *self = user_data; + GError *error; - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - else - return TRUE; + nm_utils_error_set_cancelled (&error, FALSE, NULL); + _init_start_complete (self, error); + return G_SOURCE_CONTINUE; } static void -dispose (GObject *object) +_init_start_with_bus (NMClient *self) { - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); - - nm_clear_g_cancellable (&priv->new_object_manager_cancellable); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - if (priv->manager) { - g_signal_handlers_disconnect_by_data (priv->manager, object); - g_clear_object (&priv->manager); + if (priv->init_data->cancellable) { + priv->init_data->cancelled_id = g_signal_connect (priv->init_data->cancellable, + "cancelled", + G_CALLBACK (_init_start_cancelled_cb), + self); + if (g_cancellable_is_cancelled (priv->init_data->cancellable)) { + priv->init_data->cancel_on_idle_source = g_idle_source_new (); + g_source_set_callback (priv->init_data->cancel_on_idle_source, _init_start_cancel_on_idle_cb, self, NULL); + g_source_attach (priv->init_data->cancel_on_idle_source, priv->main_context); + return; + } } - if (priv->settings) { - g_signal_handlers_disconnect_by_data (priv->settings, object); - g_clear_object (&priv->settings); - } + _assert_main_context_is_current_thread_default (self, dbus_context); - if (priv->dns_manager) { - g_signal_handlers_disconnect_by_data (priv->dns_manager, object); - g_clear_object (&priv->dns_manager); - } + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + NM_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + name_owner_get_call (self); +} + +static void +_init_start_bus_get_cb (GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient *self = user_data; + NMClientPrivate *priv; + GDBusConnection *dbus_connection; + GError *error = NULL; - if (priv->object_manager) { - GList *objects, *iter; + nm_assert (NM_IS_CLIENT (self)); - /* Unhook the NM objects. */ - objects = g_dbus_object_manager_get_objects (priv->object_manager); - for (iter = objects; iter; iter = iter->next) - g_object_set_qdata (G_OBJECT (iter->data), _nm_object_obj_nm_quark (), NULL); - g_list_free_full (objects, g_object_unref); + dbus_connection = g_bus_get_finish (result, &error); - g_signal_handlers_disconnect_by_data (priv->object_manager, object); - g_clear_object (&priv->object_manager); + if (!dbus_connection) { + _init_start_complete (self, error); + return; } - G_OBJECT_CLASS (nm_client_parent_class)->dispose (object); + priv = NM_CLIENT_GET_PRIVATE (self); + priv->dbus_connection = dbus_connection; - if (priv->udev) { - udev_unref (priv->udev); - priv->udev = NULL; - } + _init_start_with_bus (self); } static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) +_init_start (NMClient *self) { - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - switch (prop_id) { - case PROP_NETWORKING_ENABLED: - case PROP_WIRELESS_ENABLED: - case PROP_WWAN_ENABLED: - case PROP_WIMAX_ENABLED: - case PROP_CONNECTIVITY_CHECK_ENABLED: - if (priv->manager) - g_object_set_property (G_OBJECT (priv->manager), pspec->name, value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + NML_NMCLIENT_LOG_D (self, "starting %s initialization...", + priv->init_data->is_sync ? "sync" : "async"); + + if (!priv->dbus_connection) { + g_bus_get (_nm_dbus_bus_type (), + priv->init_data->cancellable, + _init_start_bus_get_cb, + self); + return; } + + _init_start_with_bus (self); } +/*****************************************************************************/ + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -3401,6 +6927,12 @@ get_property (GObject *object, guint prop_id, NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); switch (prop_id) { + case PROP_DBUS_CONNECTION: + g_value_set_object (value, priv->dbus_connection); + break; + case PROP_DBUS_NAME_OWNER: + g_value_set_string (value, nm_client_get_dbus_name_owner (self)); + break; case PROP_NM_RUNNING: g_value_set_boolean (value, nm_client_get_nm_running (self)); break; @@ -3422,28 +6954,19 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, nm_client_wireless_get_enabled (self)); break; case PROP_WIRELESS_HARDWARE_ENABLED: - if (priv->manager) - g_object_get_property (G_OBJECT (priv->manager), pspec->name, value); - else - g_value_set_boolean (value, FALSE); + g_value_set_boolean (value, nm_client_wireless_hardware_get_enabled (self)); break; case PROP_WWAN_ENABLED: g_value_set_boolean (value, nm_client_wwan_get_enabled (self)); break; case PROP_WWAN_HARDWARE_ENABLED: - if (priv->manager) - g_object_get_property (G_OBJECT (priv->manager), pspec->name, value); - else - g_value_set_boolean (value, FALSE); + g_value_set_boolean (value, nm_client_wwan_hardware_get_enabled (self)); break; case PROP_WIMAX_ENABLED: - g_value_set_boolean (value, nm_client_wimax_get_enabled (self)); + g_value_set_boolean (value, FALSE); break; case PROP_WIMAX_HARDWARE_ENABLED: - if (priv->manager) - g_object_get_property (G_OBJECT (priv->manager), pspec->name, value); - else - g_value_set_boolean (value, FALSE); + g_value_set_boolean (value, FALSE); break; case PROP_ACTIVE_CONNECTIONS: g_value_take_boxed (value, _nm_utils_copy_object_array (nm_client_get_active_connections (self))); @@ -3457,6 +6980,9 @@ get_property (GObject *object, guint prop_id, case PROP_CONNECTIVITY_CHECK_ENABLED: g_value_set_boolean (value, nm_client_connectivity_check_get_enabled (self)); break; + case PROP_CONNECTIVITY_CHECK_URI: + g_value_set_string (value, nm_client_connectivity_check_get_uri (self)); + break; case PROP_PRIMARY_CONNECTION: g_value_set_object (value, nm_client_get_primary_connection (self)); break; @@ -3467,59 +6993,39 @@ get_property (GObject *object, guint prop_id, g_value_take_boxed (value, _nm_utils_copy_object_array (nm_client_get_devices (self))); break; case PROP_METERED: - if (priv->manager) - g_object_get_property (G_OBJECT (priv->manager), pspec->name, value); - else - g_value_set_uint (value, NM_METERED_UNKNOWN); + g_value_set_uint (value, nm_client_get_metered (self)); break; case PROP_ALL_DEVICES: g_value_take_boxed (value, _nm_utils_copy_object_array (nm_client_get_all_devices (self))); break; case PROP_CHECKPOINTS: - if (priv->manager) - g_object_get_property (G_OBJECT (priv->manager), pspec->name, value); - else - g_value_take_boxed (value, g_ptr_array_new ()); + g_value_take_boxed (value, _nm_utils_copy_object_array (nm_client_get_checkpoints (self))); break; /* Settings properties. */ case PROP_CONNECTIONS: - if (priv->settings) - g_object_get_property (G_OBJECT (priv->settings), pspec->name, value); - else - g_value_take_boxed (value, _nm_utils_copy_object_array (&empty)); + g_value_take_boxed (value, _nm_utils_copy_object_array (nm_client_get_connections (self))); break; case PROP_HOSTNAME: - if (priv->settings) - g_object_get_property (G_OBJECT (priv->settings), pspec->name, value); - else - g_value_set_string (value, NULL); + g_value_set_string (value, priv->settings.hostname); break; case PROP_CAN_MODIFY: - if (priv->settings) - g_object_get_property (G_OBJECT (priv->settings), pspec->name, value); - else - g_value_set_boolean (value, FALSE); + g_value_set_boolean (value, priv->settings.can_modify); break; /* DNS properties */ case PROP_DNS_MODE: + g_value_set_string (value, nm_client_get_dns_mode (self)); + break; case PROP_DNS_RC_MANAGER: - g_return_if_fail (pspec->name && strlen (pspec->name) > NM_STRLEN ("dns-")); - if (priv->dns_manager) - g_object_get_property (G_OBJECT (priv->dns_manager), - &pspec->name[NM_STRLEN ("dns-")], value); - else - g_value_set_string (value, NULL); + g_value_set_string (value, nm_client_get_dns_rc_manager (self)); break; case PROP_DNS_CONFIGURATION: - if (priv->dns_manager) { - g_object_get_property (G_OBJECT (priv->dns_manager), - NM_DNS_MANAGER_CONFIGURATION, - value); - } else - g_value_take_boxed (value, NULL); + g_value_take_boxed (value, _nm_utils_copy_array (nm_client_get_dns_configuration (self), + (NMUtilsCopyFunc) nm_dns_entry_dup, + (GDestroyNotify) nm_dns_entry_unref)); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3527,176 +7033,613 @@ get_property (GObject *object, guint prop_id, } static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMClient *self = NM_CLIENT (object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + gboolean b; + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + /* construct-only */ + priv->dbus_connection = g_value_dup_object (value); + break; + + case PROP_NETWORKING_ENABLED: + b = g_value_get_boolean (value); + if (priv->nm.networking_enabled != b) { + nm_client_networking_set_enabled (self, + b, + NULL); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIRELESS_ENABLED: + b = g_value_get_boolean (value); + if (priv->nm.wireless_enabled != b) { + nm_client_wireless_set_enabled (self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WWAN_ENABLED: + b = g_value_get_boolean (value); + if (priv->nm.wwan_enabled != b) { + nm_client_wwan_set_enabled (self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + b = g_value_get_boolean (value); + if (priv->nm.connectivity_check_enabled != b) { + nm_client_connectivity_check_set_enabled (self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIMAX_ENABLED: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + gs_unref_object NMClient *self = NULL; + NMClientPrivate *priv; + GMainContext *dbus_context; + GError *local_error = NULL; + GMainLoop *main_loop; + GObject *parent_context_busy_watcher; + + g_return_val_if_fail (NM_IS_CLIENT (initable), FALSE); + + self = g_object_ref (NM_CLIENT (initable)); /* keep instance alive. */ + + priv = NM_CLIENT_GET_PRIVATE (self); + + g_return_val_if_fail (!priv->dbus_context, FALSE); + + /* when using init_sync(), we use a separate internal GMainContext for + * all D-Bus operations and use our regular async-init code. That means, + * also in sync-init, we don't actually block waiting for our D-Bus requests, + * instead, we only block (g_main_loop_run()) for the overall result. + * + * Doing this has a performance overhead. Also, we cannot ever fall back + * to the regular main-context (not unless we lose the main-owner and + * need to re-initialize). The reason is that we receive events on our + * dbus_context, and this cannot be brought in sync -- short of full + * reinitalizing. Therefor, using sync init not only is slower during + * construction of the object, but NMClient will stick to the dual GMainContext + * mode. + * + * Aside from this downside, the solution is good: + * + * - we don't duplicate the implementation of async-init. + * - we don't iterate the main-context of the caller while waiting for + * initialization to happen + * - we still invoke all changes under the main_context of the caller. + * - all D-Bus events strictly go through dbus_context and are in order. + */ + + dbus_context = g_main_context_new (); + priv->dbus_context = g_main_context_ref (dbus_context); + + /* We have an inner context. Note that if we loose the name owner, we have a chance + * to resync and drop the inner context. That means, requests made against the inner + * context have a different lifetime. Hence, we create a separate tracking + * object. This "wraps" the outer context-busy-watcher and references it, so + * that the work together. Grep for nm_client_context_busy_watcher_quark() to + * see how this works. */ + parent_context_busy_watcher = g_steal_pointer (&priv->context_busy_watcher); + priv->context_busy_watcher = g_object_new (G_TYPE_OBJECT, NULL); + g_object_set_qdata_full (priv->context_busy_watcher, + nm_client_context_busy_watcher_quark (), + parent_context_busy_watcher, + g_object_unref); + + g_main_context_push_thread_default (dbus_context); + + main_loop = g_main_loop_new (dbus_context, FALSE); + + priv->init_data = _init_data_new_sync (cancellable, main_loop, &local_error); + + _init_start (self); + + g_main_loop_run (main_loop); + + g_main_loop_unref (main_loop); + + g_main_context_pop_thread_default (dbus_context); + + if (priv->main_context != priv->dbus_context) { + GSource *source; + + source = nm_utils_g_main_context_create_integrate_source (priv->dbus_context); + g_source_attach (source, priv->main_context); + _context_busy_watcher_attach_integration_source (priv->context_busy_watcher, + g_steal_pointer (&source)); + } + + g_main_context_unref (dbus_context); + + if (local_error) { + g_propagate_error (error, local_error); + return FALSE; + } + return TRUE; +} + +/*****************************************************************************/ + +static void +init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate *priv; + NMClient *self; + nm_auto_pop_gmaincontext GMainContext *context = NULL; + GTask *task; + + g_return_if_fail (NM_IS_CLIENT (initable)); + + self = NM_CLIENT (initable); + priv = NM_CLIENT_GET_PRIVATE (self); + + g_return_if_fail (!priv->dbus_context); + + priv->dbus_context = g_main_context_ref (priv->main_context); + + context = nm_g_main_context_push_thread_default_if_necessary (priv->main_context); + + task = nm_g_task_new (self, cancellable, init_async, callback, user_data); + g_task_set_priority (task, io_priority); + + priv->init_data = _init_data_new_async (cancellable, g_steal_pointer (&task)); + + _init_start (self); +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail (NM_IS_CLIENT (initable), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, initable, init_async), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +/*****************************************************************************/ + +static void +nm_client_init (NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + priv->context_busy_watcher = g_object_new (G_TYPE_OBJECT, NULL); + + c_list_init (&self->obj_base.queue_notify_lst); + c_list_init (&priv->queue_notify_lst_head); + c_list_init (&priv->notify_event_lst_head); + + priv->dbus_objects = g_hash_table_new (nm_pdirect_hash, nm_pdirect_equal); + c_list_init (&priv->dbus_objects_lst_head_watched_only); + c_list_init (&priv->dbus_objects_lst_head_on_dbus); + c_list_init (&priv->dbus_objects_lst_head_with_nmobj_not_ready); + c_list_init (&priv->dbus_objects_lst_head_with_nmobj_ready); + c_list_init (&priv->obj_changed_lst_head); +} + +/** + * nm_client_new: + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Creates a new #NMClient. + * + * Note that this will do blocking D-Bus calls to initialize the + * client. You can use nm_client_new_async() if you want to avoid + * that. + * + * Returns: a new #NMClient or NULL on an error + **/ +NMClient * +nm_client_new (GCancellable *cancellable, + GError **error) +{ + return g_initable_new (NM_TYPE_CLIENT, cancellable, error, + NULL); +} + +/** + * nm_client_new_async: + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the client is created + * @user_data: data for @callback + * + * Creates a new #NMClient and begins asynchronously initializing it. + * @callback will be called when it is done; use + * nm_client_new_finish() to get the result. Note that on an error, + * the callback can be invoked with two first parameters as NULL. + **/ +void +nm_client_new_async (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (NM_TYPE_CLIENT, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + NULL); +} + +/** + * nm_client_new_finish: + * @result: a #GAsyncResult + * @error: location for a #GError, or %NULL + * + * Gets the result of an nm_client_new_async() call. + * + * Returns: a new #NMClient, or %NULL on error + **/ +NMClient * +nm_client_new_finish (GAsyncResult *result, GError **error) +{ + gs_unref_object GObject *source_object = NULL; + GObject *object; + + source_object = g_async_result_get_source_object (result); + g_return_val_if_fail (source_object, NULL); + + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + result, + error); + g_return_val_if_fail (!object || NM_IS_CLIENT (object), FALSE); + + return NM_CLIENT (object); +} + +static void +constructed (GObject *object) +{ + NMClient *self = NM_CLIENT (object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + priv->main_context = g_main_context_ref_thread_default (); + + G_OBJECT_CLASS (nm_client_parent_class)->constructed (object); + + NML_NMCLIENT_LOG_D (self, "new NMClient instance"); +} + +static void +dispose (GObject *object) +{ + NMClient *self = NM_CLIENT (object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + nm_assert (!priv->init_data); + + self->obj_base.is_disposing = TRUE; + + nm_clear_g_cancellable (&priv->name_owner_get_cancellable); + + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->name_owner_changed_id); + + nm_clear_g_free (&priv->name_owner); + + _init_release_all (self); + + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_watched_only)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_on_dbus)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_with_nmobj_not_ready)); + nm_assert (c_list_is_empty (&priv->dbus_objects_lst_head_with_nmobj_ready)); + + nm_assert (c_list_is_empty (&priv->queue_notify_lst_head)); + nm_assert (c_list_is_empty (&priv->notify_event_lst_head)); + nm_assert (c_list_is_empty (&self->obj_base.queue_notify_lst)); + nm_assert (!priv->dbus_objects || g_hash_table_size (priv->dbus_objects) == 0); + + nml_dbus_property_o_clear_many (priv->nm.property_o, G_N_ELEMENTS (priv->nm.property_o), NULL); + nml_dbus_property_ao_clear_many (priv->nm.property_ao, G_N_ELEMENTS (priv->nm.property_ao), NULL); + + nm_clear_g_free (&priv->nm.connectivity_check_uri); + nm_clear_g_free (&priv->nm.version); + + nml_dbus_property_ao_clear (&priv->settings.connections, NULL); + nm_clear_g_free (&priv->settings.hostname); + + nm_clear_pointer (&priv->dns_manager.configuration, g_ptr_array_unref); + nm_clear_g_free (&priv->dns_manager.mode); + nm_clear_g_free (&priv->dns_manager.rc_manager); + + nm_clear_pointer (&priv->dbus_objects, g_hash_table_destroy); + + G_OBJECT_CLASS (nm_client_parent_class)->dispose (object); + + nm_clear_pointer (&priv->udev, udev_unref); + + nm_clear_pointer (&priv->dbus_context, g_main_context_unref); + nm_clear_pointer (&priv->main_context, g_main_context_unref); + + nm_clear_pointer (&priv->permissions, g_hash_table_unref); + + g_clear_object (&priv->dbus_connection); + + g_clear_object (&priv->context_busy_watcher); + + nm_clear_g_free (&priv->name_owner); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_agentmanager = NML_DBUS_META_IFACE_INIT ( + NM_DBUS_INTERFACE_AGENT_MANAGER, + NULL, + NML_DBUS_META_INTERFACE_PRIO_NONE, +); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP ( + NM_DBUS_INTERFACE, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES ( + NML_DBUS_META_PROPERTY_INIT_O_PROP ("ActivatingConnection", PROP_ACTIVATING_CONNECTION, NMClient, _priv.nm.property_o[PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION], nm_active_connection_get_type ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP ("ActiveConnections", PROP_ACTIVE_CONNECTIONS, NMClient, _priv.nm.property_ao[PROPERTY_AO_IDX_ACTIVE_CONNECTIONS], nm_active_connection_get_type, .notify_changed_ao = _property_ao_notify_changed_active_connections_cb ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP ("AllDevices", PROP_ALL_DEVICES, NMClient, _priv.nm.property_ao[PROPERTY_AO_IDX_ALL_DEVICES], nm_device_get_type, .notify_changed_ao = _property_ao_notify_changed_all_devices_cb ), + NML_DBUS_META_PROPERTY_INIT_IGNORE ("Capabilities", "au" ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP ("Checkpoints", PROP_CHECKPOINTS, NMClient, _priv.nm.property_ao[PROPERTY_AO_IDX_CHECKPOINTS], nm_checkpoint_get_type ), + NML_DBUS_META_PROPERTY_INIT_U ("Connectivity", PROP_CONNECTIVITY, NMClient, _priv.nm.connectivity ), + NML_DBUS_META_PROPERTY_INIT_B ("ConnectivityCheckAvailable", PROP_CONNECTIVITY_CHECK_AVAILABLE, NMClient, _priv.nm.connectivity_check_available ), + NML_DBUS_META_PROPERTY_INIT_B ("ConnectivityCheckEnabled", PROP_CONNECTIVITY_CHECK_ENABLED, NMClient, _priv.nm.connectivity_check_enabled ), + NML_DBUS_META_PROPERTY_INIT_S ("ConnectivityCheckUri", PROP_CONNECTIVITY_CHECK_URI, NMClient, _priv.nm.connectivity_check_uri ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP ("Devices", PROP_DEVICES, NMClient, _priv.nm.property_ao[PROPERTY_AO_IDX_DEVICES], nm_device_get_type, .notify_changed_ao = _property_ao_notify_changed_devices_cb ), + NML_DBUS_META_PROPERTY_INIT_IGNORE ("GlobalDnsConfiguration", "a{sv}" ), + NML_DBUS_META_PROPERTY_INIT_U ("Metered", PROP_METERED, NMClient, _priv.nm.metered ), + NML_DBUS_META_PROPERTY_INIT_B ("NetworkingEnabled", PROP_NETWORKING_ENABLED, NMClient, _priv.nm.networking_enabled ), + NML_DBUS_META_PROPERTY_INIT_O_PROP ("PrimaryConnection", PROP_PRIMARY_CONNECTION, NMClient, _priv.nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION], nm_active_connection_get_type ), + NML_DBUS_META_PROPERTY_INIT_IGNORE ("PrimaryConnectionType", "s" ), + NML_DBUS_META_PROPERTY_INIT_B ("Startup", PROP_STARTUP, NMClient, _priv.nm.startup ), + NML_DBUS_META_PROPERTY_INIT_U ("State", PROP_STATE, NMClient, _priv.nm.state ), + NML_DBUS_META_PROPERTY_INIT_S ("Version", PROP_VERSION, NMClient, _priv.nm.version ), + NML_DBUS_META_PROPERTY_INIT_IGNORE ("WimaxEnabled", "b" ), + NML_DBUS_META_PROPERTY_INIT_IGNORE ("WimaxHardwareEnabled", "b" ), + NML_DBUS_META_PROPERTY_INIT_B ("WirelessEnabled", PROP_WIRELESS_ENABLED, NMClient, _priv.nm.wireless_enabled ), + NML_DBUS_META_PROPERTY_INIT_B ("WirelessHardwareEnabled", PROP_WIRELESS_HARDWARE_ENABLED, NMClient, _priv.nm.wireless_hardware_enabled ), + NML_DBUS_META_PROPERTY_INIT_B ("WwanEnabled", PROP_WWAN_ENABLED, NMClient, _priv.nm.wwan_enabled ), + NML_DBUS_META_PROPERTY_INIT_B ("WwanHardwareEnabled", PROP_WWAN_HARDWARE_ENABLED, NMClient, _priv.nm.wwan_hardware_enabled ), + ), +); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings = NML_DBUS_META_IFACE_INIT_PROP ( + NM_DBUS_INTERFACE_SETTINGS, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES ( + NML_DBUS_META_PROPERTY_INIT_B ("CanModify", PROP_CAN_MODIFY, NMClient, _priv.settings.can_modify ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP ("Connections", PROP_CONNECTIONS, NMClient, _priv.settings.connections, nm_remote_connection_get_type, .notify_changed_ao = _property_ao_notify_changed_connections_cb, .check_nmobj_visible_fcn = (gboolean (*) (GObject *)) nm_remote_connection_get_visible ), + NML_DBUS_META_PROPERTY_INIT_S ("Hostname", PROP_HOSTNAME, NMClient, _priv.settings.hostname ), + ), +); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dnsmanager = NML_DBUS_META_IFACE_INIT_PROP ( + NM_DBUS_INTERFACE_DNS_MANAGER, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES ( + NML_DBUS_META_PROPERTY_INIT_FCN ("Configuration", PROP_DNS_CONFIGURATION, "aa{sv}", _notify_update_prop_dns_manager_configuration ), + NML_DBUS_META_PROPERTY_INIT_S ("Mode", PROP_DNS_MODE, NMClient, _priv.dns_manager.mode ), + NML_DBUS_META_PROPERTY_INIT_S ("RcManager", PROP_DNS_RC_MANAGER, NMClient, _priv.dns_manager.rc_manager ), + ), +); + +static void nm_client_class_init (NMClientClass *client_class) { GObjectClass *object_class = G_OBJECT_CLASS (client_class); - g_type_class_add_private (client_class, sizeof (NMClientPrivate)); + _dbus_path_nm = nm_ref_string_new (NM_DBUS_PATH); + _dbus_path_settings = nm_ref_string_new (NM_DBUS_PATH_SETTINGS); + _dbus_path_dns_manager = nm_ref_string_new (NM_DBUS_PATH_DNS_MANAGER); - /* virtual methods */ - object_class->set_property = set_property; object_class->get_property = get_property; - object_class->dispose = dispose; + object_class->set_property = set_property; + object_class->constructed = constructed; + object_class->dispose = dispose; + + /** + * NMClient:dbus-connection: + * + * The #GDBusConnection to use. + * + * If this is not set during object construction, the D-Bus connection will + * automatically be chosen during async/sync initalization via g_bus_get(). + * + * Since: 1.22 + */ + obj_properties[PROP_DBUS_CONNECTION] = + g_param_spec_object (NM_CLIENT_DBUS_CONNECTION, "", "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); - /* properties */ + /** + * NMClient:dbus-name-owner: + * + * The name owner of the NetworkManager D-Bus service. + * + * Since: 1.22 + **/ + obj_properties[PROP_DBUS_NAME_OWNER] = + g_param_spec_string (NM_CLIENT_DBUS_NAME_OWNER, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:version: * * The NetworkManager version. **/ - g_object_class_install_property - (object_class, PROP_VERSION, - g_param_spec_string (NM_CLIENT_VERSION, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_VERSION] = + g_param_spec_string (NM_CLIENT_VERSION, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:state: * * The current daemon state. **/ - g_object_class_install_property - (object_class, PROP_STATE, - g_param_spec_enum (NM_CLIENT_STATE, "", "", - NM_TYPE_STATE, - NM_STATE_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_STATE] = + g_param_spec_enum (NM_CLIENT_STATE, "", "", + NM_TYPE_STATE, + NM_STATE_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:startup: * * Whether the daemon is still starting up. **/ - g_object_class_install_property - (object_class, PROP_STARTUP, - g_param_spec_boolean (NM_CLIENT_STARTUP, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_STARTUP] = + g_param_spec_boolean (NM_CLIENT_STARTUP, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:nm-running: * * Whether the daemon is running. **/ - g_object_class_install_property - (object_class, PROP_NM_RUNNING, - g_param_spec_boolean (NM_CLIENT_NM_RUNNING, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_NM_RUNNING] = + g_param_spec_boolean (NM_CLIENT_NM_RUNNING, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:networking-enabled: * * Whether networking is enabled. - **/ - g_object_class_install_property - (object_class, PROP_NETWORKING_ENABLED, - g_param_spec_boolean (NM_CLIENT_NETWORKING_ENABLED, "", "", - TRUE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_NETWORKING_ENABLED] = + g_param_spec_boolean (NM_CLIENT_NETWORKING_ENABLED, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMClient:wireless-enabled: * * Whether wireless is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. **/ - g_object_class_install_property - (object_class, PROP_WIRELESS_ENABLED, - g_param_spec_boolean (NM_CLIENT_WIRELESS_ENABLED, "", "", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_WIRELESS_ENABLED] = + g_param_spec_boolean (NM_CLIENT_WIRELESS_ENABLED, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMClient:wireless-hardware-enabled: * * Whether the wireless hardware is enabled. **/ - g_object_class_install_property - (object_class, PROP_WIRELESS_HARDWARE_ENABLED, - g_param_spec_boolean (NM_CLIENT_WIRELESS_HARDWARE_ENABLED, "", "", - TRUE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_WIRELESS_HARDWARE_ENABLED] = + g_param_spec_boolean (NM_CLIENT_WIRELESS_HARDWARE_ENABLED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:wwan-enabled: * * Whether WWAN functionality is enabled. - **/ - g_object_class_install_property - (object_class, PROP_WWAN_ENABLED, - g_param_spec_boolean (NM_CLIENT_WWAN_ENABLED, "", "", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_WWAN_ENABLED] = + g_param_spec_boolean (NM_CLIENT_WWAN_ENABLED, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMClient:wwan-hardware-enabled: * * Whether the WWAN hardware is enabled. **/ - g_object_class_install_property - (object_class, PROP_WWAN_HARDWARE_ENABLED, - g_param_spec_boolean (NM_CLIENT_WWAN_HARDWARE_ENABLED, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_WWAN_HARDWARE_ENABLED] = + g_param_spec_boolean (NM_CLIENT_WWAN_HARDWARE_ENABLED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:wimax-enabled: * * Whether WiMAX functionality is enabled. - **/ - g_object_class_install_property - (object_class, PROP_WIMAX_ENABLED, - g_param_spec_boolean (NM_CLIENT_WIMAX_ENABLED, "", "", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + * + * Deprecated: 1.22: WiMAX is no longer supported and this always returns FALSE. The setter has no effect. + */ + obj_properties[PROP_WIMAX_ENABLED] = + g_param_spec_boolean (NM_CLIENT_WIMAX_ENABLED, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMClient:wimax-hardware-enabled: * * Whether the WiMAX hardware is enabled. + * + * Deprecated: 1.22: WiMAX is no longer supported and this always returns FALSE. **/ - g_object_class_install_property - (object_class, PROP_WIMAX_HARDWARE_ENABLED, - g_param_spec_boolean (NM_CLIENT_WIMAX_HARDWARE_ENABLED, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_WIMAX_HARDWARE_ENABLED] = + g_param_spec_boolean (NM_CLIENT_WIMAX_HARDWARE_ENABLED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:active-connections: (type GPtrArray(NMActiveConnection)) * * The active connections. **/ - g_object_class_install_property - (object_class, PROP_ACTIVE_CONNECTIONS, - g_param_spec_boxed (NM_CLIENT_ACTIVE_CONNECTIONS, "", "", - G_TYPE_PTR_ARRAY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_ACTIVE_CONNECTIONS] = + g_param_spec_boxed (NM_CLIENT_ACTIVE_CONNECTIONS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:connectivity: * * The network connectivity state. */ - g_object_class_install_property - (object_class, PROP_CONNECTIVITY, - g_param_spec_enum (NM_CLIENT_CONNECTIVITY, "", "", - NM_TYPE_CONNECTIVITY_STATE, - NM_CONNECTIVITY_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CONNECTIVITY] = + g_param_spec_enum (NM_CLIENT_CONNECTIVITY, "", "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient::connectivity-check-available @@ -3705,12 +7648,11 @@ nm_client_class_init (NMClientClass *client_class) * * Since: 1.10 */ - g_object_class_install_property - (object_class, PROP_CONNECTIVITY_CHECK_AVAILABLE, - g_param_spec_boolean (NM_CLIENT_CONNECTIVITY_CHECK_AVAILABLE, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CONNECTIVITY_CHECK_AVAILABLE] = + g_param_spec_boolean (NM_CLIENT_CONNECTIVITY_CHECK_AVAILABLE, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient::connectivity-check-enabled @@ -3718,13 +7660,27 @@ nm_client_class_init (NMClientClass *client_class) * Whether a connectivity checking service has been enabled. * * Since: 1.10 + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. */ - g_object_class_install_property - (object_class, PROP_CONNECTIVITY_CHECK_ENABLED, - g_param_spec_boolean (NM_CLIENT_CONNECTIVITY_CHECK_ENABLED, "", "", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CONNECTIVITY_CHECK_ENABLED] = + g_param_spec_boolean (NM_CLIENT_CONNECTIVITY_CHECK_ENABLED, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connectivity-check-uri: + * + * The used URI for connectivity checking. + * + * Since: 1.22 + **/ + obj_properties[PROP_CONNECTIVITY_CHECK_URI] = + g_param_spec_string (NM_CLIENT_CONNECTIVITY_CHECK_URI, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:primary-connection: @@ -3732,12 +7688,11 @@ nm_client_class_init (NMClientClass *client_class) * The #NMActiveConnection of the device with the default route; * see nm_client_get_primary_connection() for more details. **/ - g_object_class_install_property - (object_class, PROP_PRIMARY_CONNECTION, - g_param_spec_object (NM_CLIENT_PRIMARY_CONNECTION, "", "", - NM_TYPE_ACTIVE_CONNECTION, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PRIMARY_CONNECTION] = + g_param_spec_object (NM_CLIENT_PRIMARY_CONNECTION, "", "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:activating-connection: @@ -3745,24 +7700,22 @@ nm_client_class_init (NMClientClass *client_class) * The #NMActiveConnection of the activating connection that is * likely to become the new #NMClient:primary-connection. **/ - g_object_class_install_property - (object_class, PROP_ACTIVATING_CONNECTION, - g_param_spec_object (NM_CLIENT_ACTIVATING_CONNECTION, "", "", - NM_TYPE_ACTIVE_CONNECTION, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_ACTIVATING_CONNECTION] = + g_param_spec_object (NM_CLIENT_ACTIVATING_CONNECTION, "", "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:devices: (type GPtrArray(NMDevice)) * * List of real network devices. Does not include placeholder devices. **/ - g_object_class_install_property - (object_class, PROP_DEVICES, - g_param_spec_boxed (NM_CLIENT_DEVICES, "", "", - G_TYPE_PTR_ARRAY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_DEVICES] = + g_param_spec_boxed (NM_CLIENT_DEVICES, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:all-devices: (type GPtrArray(NMDevice)) @@ -3770,12 +7723,11 @@ nm_client_class_init (NMClientClass *client_class) * List of both real devices and device placeholders. * Since: 1.2 **/ - g_object_class_install_property - (object_class, PROP_ALL_DEVICES, - g_param_spec_boxed (NM_CLIENT_ALL_DEVICES, "", "", - G_TYPE_PTR_ARRAY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_ALL_DEVICES] = + g_param_spec_boxed (NM_CLIENT_ALL_DEVICES, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:connections: (type GPtrArray(NMRemoteConnection)) @@ -3785,12 +7737,11 @@ nm_client_class_init (NMClientClass *client_class) * contain the object paths of connections that the user does not have * permission to read the details of.) */ - g_object_class_install_property - (object_class, PROP_CONNECTIONS, - g_param_spec_boxed (NM_CLIENT_CONNECTIONS, "", "", - G_TYPE_PTR_ARRAY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CONNECTIONS] = + g_param_spec_boxed (NM_CLIENT_CONNECTIONS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:hostname: @@ -3798,24 +7749,22 @@ nm_client_class_init (NMClientClass *client_class) * The machine hostname stored in persistent configuration. This can be * modified by calling nm_client_save_hostname(). */ - g_object_class_install_property - (object_class, PROP_HOSTNAME, - g_param_spec_string (NM_CLIENT_HOSTNAME, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_HOSTNAME] = + g_param_spec_string (NM_CLIENT_HOSTNAME, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:can-modify: * * If %TRUE, adding and modifying connections is supported. */ - g_object_class_install_property - (object_class, PROP_CAN_MODIFY, - g_param_spec_boolean (NM_CLIENT_CAN_MODIFY, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CAN_MODIFY] = + g_param_spec_boolean (NM_CLIENT_CAN_MODIFY, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:metered: @@ -3824,12 +7773,11 @@ nm_client_class_init (NMClientClass *client_class) * * Since: 1.2 **/ - g_object_class_install_property - (object_class, PROP_METERED, - g_param_spec_uint (NM_CLIENT_METERED, "", "", - 0, G_MAXUINT32, NM_METERED_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_METERED] = + g_param_spec_uint (NM_CLIENT_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:dns-mode: @@ -3838,12 +7786,11 @@ nm_client_class_init (NMClientClass *client_class) * * Since: 1.6 **/ - g_object_class_install_property - (object_class, PROP_DNS_MODE, - g_param_spec_string (NM_CLIENT_DNS_MODE, "", "", - "", - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_DNS_MODE] = + g_param_spec_string (NM_CLIENT_DNS_MODE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:dns-rc-manager: @@ -3852,12 +7799,11 @@ nm_client_class_init (NMClientClass *client_class) * * Since: 1.6 **/ - g_object_class_install_property - (object_class, PROP_DNS_RC_MANAGER, - g_param_spec_string (NM_CLIENT_DNS_RC_MANAGER, "", "", - "", - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_DNS_RC_MANAGER] = + g_param_spec_string (NM_CLIENT_DNS_RC_MANAGER, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:dns-configuration: (type GPtrArray(NMDnsEntry)) @@ -3867,12 +7813,11 @@ nm_client_class_init (NMClientClass *client_class) * * Since: 1.6 **/ - g_object_class_install_property - (object_class, PROP_DNS_CONFIGURATION, - g_param_spec_boxed (NM_CLIENT_DNS_CONFIGURATION, "", "", - G_TYPE_PTR_ARRAY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_DNS_CONFIGURATION] = + g_param_spec_boxed (NM_CLIENT_DNS_CONFIGURATION, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); /** * NMClient:checkpoints: (type GPtrArray(NMCheckpoint)) @@ -3881,14 +7826,15 @@ nm_client_class_init (NMClientClass *client_class) * * Since: 1.12 */ - g_object_class_install_property - (object_class, PROP_CHECKPOINTS, - g_param_spec_boxed (NM_MANAGER_CHECKPOINTS, "", "", - G_TYPE_PTR_ARRAY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CHECKPOINTS] = + g_param_spec_boxed (NM_CLIENT_CHECKPOINTS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); - /* signals */ + _nml_dbus_meta_class_init_with_properties (object_class, &_nml_dbus_meta_iface_nm, + &_nml_dbus_meta_iface_nm_settings, + &_nml_dbus_meta_iface_nm_dnsmanager); /** * NMClient::device-added: @@ -3899,13 +7845,12 @@ nm_client_class_init (NMClientClass *client_class) * placeholder devices. **/ signals[DEVICE_ADDED] = - g_signal_new (NM_CLIENT_DEVICE_ADDED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMClientClass, device_added), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); + g_signal_new (NM_CLIENT_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); /** * NMClient::device-removed: @@ -3916,13 +7861,12 @@ nm_client_class_init (NMClientClass *client_class) * placeholder devices. **/ signals[DEVICE_REMOVED] = - g_signal_new (NM_CLIENT_DEVICE_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMClientClass, device_removed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); + g_signal_new (NM_CLIENT_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); /** * NMClient::any-device-added: @@ -3933,13 +7877,12 @@ nm_client_class_init (NMClientClass *client_class) * regular devices and placeholder devices. **/ signals[ANY_DEVICE_ADDED] = - g_signal_new (NM_CLIENT_ANY_DEVICE_ADDED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMClientClass, any_device_added), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); + g_signal_new (NM_CLIENT_ANY_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); /** * NMClient::any-device-removed: @@ -3950,13 +7893,12 @@ nm_client_class_init (NMClientClass *client_class) * regular devices and placeholder devices. **/ signals[ANY_DEVICE_REMOVED] = - g_signal_new (NM_CLIENT_ANY_DEVICE_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMClientClass, any_device_removed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); + g_signal_new (NM_CLIENT_ANY_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); /** * NMClient::permission-changed: @@ -3967,11 +7909,11 @@ nm_client_class_init (NMClientClass *client_class) * Notifies that a permission has changed **/ signals[PERMISSION_CHANGED] = - g_signal_new (NM_CLIENT_PERMISSION_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + g_signal_new (NM_CLIENT_PERMISSION_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * NMClient::connection-added: * @client: the settings object that received the signal @@ -3980,13 +7922,12 @@ nm_client_class_init (NMClientClass *client_class) * Notifies that a #NMConnection has been added. **/ signals[CONNECTION_ADDED] = - g_signal_new (NM_CLIENT_CONNECTION_ADDED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMClientClass, connection_added), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - NM_TYPE_REMOTE_CONNECTION); + g_signal_new (NM_CLIENT_CONNECTION_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_REMOTE_CONNECTION); /** * NMClient::connection-removed: @@ -3996,13 +7937,12 @@ nm_client_class_init (NMClientClass *client_class) * Notifies that a #NMConnection has been removed. **/ signals[CONNECTION_REMOVED] = - g_signal_new (NM_CLIENT_CONNECTION_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMClientClass, connection_removed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - NM_TYPE_REMOTE_CONNECTION); + g_signal_new (NM_CLIENT_CONNECTION_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_REMOTE_CONNECTION); /** * NMClient::active-connection-added: @@ -4012,12 +7952,12 @@ nm_client_class_init (NMClientClass *client_class) * Notifies that a #NMActiveConnection has been added. **/ signals[ACTIVE_CONNECTION_ADDED] = - g_signal_new (NM_CLIENT_ACTIVE_CONNECTION_ADDED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, - NM_TYPE_ACTIVE_CONNECTION); + g_signal_new (NM_CLIENT_ACTIVE_CONNECTION_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_ACTIVE_CONNECTION); /** * NMClient::active-connection-removed: @@ -4027,12 +7967,12 @@ nm_client_class_init (NMClientClass *client_class) * Notifies that a #NMActiveConnection has been removed. **/ signals[ACTIVE_CONNECTION_REMOVED] = - g_signal_new (NM_CLIENT_ACTIVE_CONNECTION_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, - NM_TYPE_ACTIVE_CONNECTION); + g_signal_new (NM_CLIENT_ACTIVE_CONNECTION_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_ACTIVE_CONNECTION); } static void |