summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-09-02 10:35:37 +0200
committerThomas Haller <thaller@redhat.com>2019-09-27 16:28:15 +0200
commitfd0a2af47013e2ea59bcd0f71850d7fcbec29fc1 (patch)
treecc4ad3ccbff661a1bb4ae1c19da91f4fc83bd767
parent1a3006fd7440c4a1b0d131992b07e17de1cba834 (diff)
downloadNetworkManager-th/libnm-dbus-rework.tar.gz
libnm: add nm-dbus-client.[hc]th/libnm-dbus-rework
This new code will interact with the D-Bus interface of NetworkManager to realize the client-side cache of objects (what libnm has). This is more complicated because of libnm's API, because of the following: (1) libnm should never perform any blocking D-Bus calls. That allows to schedule and process calls in parallel, and also lets us get the order of events right. That is especially relevant, because we want and *must* make certain calls asynchronously. It's impossible to correctly mix asynchronous and synchronous calls without messing up the order of events. Hence, all D-Bus of libnm calls must be made asynchronously internally. (2) libnm has blocking calls of two kinds. Blocking calls fundamentally are wrong and I wish we shouldn't have them. Also, they complicate the implementation of this by an order of magnitude, and have a clear performance overhead. They are wrong, because during the blocking call, we clearly don't want to invoke *any* events. For example, no NM_CLIENT_DEVICE_ADDED signals, and no property changed signals from the libnm cache. Otherwise, the call wouldn't be very blocking. Everything that happens to the state of the cache while the blocking call is pending, must be invoked only afterwards (but still in the right order!). We have some blocking calls that merely make a D-Bus call. For example, nm_manager_deactivate_connection(). This does the blocking D-Bus call and returns, but afterwards the state of the client cache is not yet caught up. Whether such a behavior makes any sense at all is questionable, because the cache will still contain the active connection in state ACTIVATED. Changes to the cache will only be visible when iterating the main context again. While this kind of calls is simple to implement, it's also pretty pointless and probably shouldn't behave like this. Other blocking calls are more complicated. (2a) blocking calls that merely perform a blocking D-Bus call. A blocking call like g_dbus_connection_call_sync() will not process any events, while waiting for the reply. That is implemented by
-rw-r--r--Makefile.am2
-rw-r--r--libnm/meson.build1
-rw-r--r--libnm/nm-client.c12
-rw-r--r--libnm/nm-client.h3
-rw-r--r--libnm/nm-dbus-client.c237
-rw-r--r--libnm/nm-dbus-client.h37
6 files changed, 292 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 95238324a4..523caf00e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1232,6 +1232,7 @@ libnm_lib_h_pub_mkenums = \
libnm/nm-enum-types.h
libnm_lib_h_priv = \
libnm/nm-libnm-utils.h \
+ libnm/nm-dbus-client.h \
libnm/nm-dbus-helpers.h \
libnm/nm-device-private.h \
libnm/nm-dhcp4-config.h \
@@ -1248,6 +1249,7 @@ libnm_lib_c_real = \
libnm/nm-active-connection.c \
libnm/nm-checkpoint.c \
libnm/nm-client.c \
+ libnm/nm-dbus-client.c \
libnm/nm-dbus-helpers.c \
libnm/nm-device-6lowpan.c \
libnm/nm-device-adsl.c \
diff --git a/libnm/meson.build b/libnm/meson.build
index 0d7fb3acf2..d9398b57e4 100644
--- a/libnm/meson.build
+++ b/libnm/meson.build
@@ -77,6 +77,7 @@ libnm_sources = files(
'nm-active-connection.c',
'nm-checkpoint.c',
'nm-client.c',
+ 'nm-dbus-client.c',
'nm-dbus-helpers.c',
'nm-device-6lowpan.c',
'nm-device-adsl.c',
diff --git a/libnm/nm-client.c b/libnm/nm-client.c
index 1e81221537..2c09698986 100644
--- a/libnm/nm-client.c
+++ b/libnm/nm-client.c
@@ -20,6 +20,7 @@
#include "nm-active-connection.h"
#include "nm-vpn-connection.h"
#include "nm-remote-connection.h"
+#include "nm-dbus-client.h"
#include "nm-dbus-helpers.h"
#include "nm-wimax-nsp.h"
#include "nm-object-private.h"
@@ -96,6 +97,7 @@ typedef struct {
typedef struct {
NMManager *manager;
+ NMDBusClient *dbus_client;
NMRemoteSettings *settings;
NMDnsManager *dns_manager;
GDBusObjectManager *object_manager;
@@ -168,9 +170,17 @@ NM_CACHED_QUARK_FCN ("nm-client-error-quark", nm_client_error_quark)
/*****************************************************************************/
+static const NMDBusClientCallbackTable dbus_client_callback_table = {
+};
+
static void
nm_client_init (NMClient *client)
{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+
+ priv->dbus_client = nm_dbus_client_new (g_bus_get_sync (_nm_dbus_bus_type (), NULL, NULL),
+ &dbus_client_callback_table,
+ client);
}
static gboolean
@@ -3408,6 +3418,8 @@ dispose (GObject *object)
nm_clear_g_cancellable (&priv->new_object_manager_cancellable);
+ nm_clear_pointer (&priv->dbus_client, nm_dbus_client_free);
+
if (priv->manager) {
g_signal_handlers_disconnect_by_data (priv->manager, object);
g_clear_object (&priv->manager);
diff --git a/libnm/nm-client.h b/libnm/nm-client.h
index 44b71ee094..da66e9fe5c 100644
--- a/libnm/nm-client.h
+++ b/libnm/nm-client.h
@@ -221,9 +221,11 @@ typedef struct {
GType nm_client_get_type (void);
+_NM_LIBNM_SYNC
NMClient *nm_client_new (GCancellable *cancellable,
GError **error);
+_NM_LIBNM_ASYNC
void nm_client_new_async (GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -291,6 +293,7 @@ NMClientPermissionResult nm_client_get_permission_result (NMClient *client,
NMConnectivityState nm_client_get_connectivity (NMClient *client);
+_NM_LIBNM_SYNC
NMConnectivityState nm_client_check_connectivity (NMClient *client,
GCancellable *cancellable,
GError **error);
diff --git a/libnm/nm-dbus-client.c b/libnm/nm-dbus-client.c
new file mode 100644
index 0000000000..109dc357fc
--- /dev/null
+++ b/libnm/nm-dbus-client.c
@@ -0,0 +1,237 @@
+/*
+ * 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 2019 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-dbus-client.h"
+
+#include "c-list/src/c-list.h"
+#include "nm-glib-aux/nm-dbus-aux.h"
+#include "nm-glib-aux/nm-ref-string.h"
+#include "nm-dbus-interface.h"
+
+/*****************************************************************************/
+
+typedef void (*EventHandleFcn) (NMDBusClient *self,
+ gpointer user_data);
+
+typedef struct {
+ CList event_lst;
+ EventHandleFcn handle_fcn;
+ gpointer user_data;
+} EventData;
+
+struct _NMDBusClient {
+
+ const NMDBusClientCallbackTable *callback_table;
+ gpointer callback_user_data;
+
+ GMutex event_lock;
+
+ CList event_lst_head;
+
+ GMainContext *caller_context;
+ GMainContext *intern_context;
+
+ GDBusConnection *dbus_connection;
+
+ GCancellable *i_name_owner_get_cancellable;
+
+ char *i_name_owner;
+
+ guint name_owner_changed_id;
+};
+
+/*****************************************************************************/
+
+#define _LOGT(...) do { } while (0)
+
+static gboolean
+NM_IS_DBUS_CLIENT (NMDBusClient *self)
+{
+ nm_assert (!self || G_IS_DBUS_CONNECTION (self->dbus_connection));
+
+ return !!self;
+}
+
+/*****************************************************************************/
+
+static void
+_event_queue (NMDBusClient *self,
+ EventHandleFcn handle_fcn,
+ gpointer user_data)
+{
+ EventData *e;
+
+ e = g_slice_new (EventData);
+ e->handle_fcn = handle_fcn;
+ e->user_data = user_data;
+
+ g_mutex_lock (&self->event_lock);
+ c_list_link_tail (&self->event_lst_head, &e->event_lst);
+ g_mutex_unlock (&self->event_lock);
+}
+
+static gboolean
+_event_dequeue_and_handle (NMDBusClient *self)
+{
+ EventData *e;
+
+ g_mutex_lock (&self->event_lock);
+ e = c_list_first_entry (&self->event_lst_head, EventData, event_lst);
+ if (e)
+ c_list_unlink (&e->event_lst);
+ g_mutex_unlock (&self->event_lock);
+
+ if (!e)
+ return FALSE;
+
+ e->handle_fcn (self, e->user_data);
+ nm_g_slice_free (e);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+i_name_owner_changed (NMDBusClient *self,
+ const char *name_owner,
+ gboolean from_get_callback)
+{
+ if (G_UNLIKELY (self->i_name_owner_get_cancellable))
+ g_clear_object (&self->i_name_owner_get_cancellable);
+ else {
+ if (nm_streq0 (self->i_name_owner, name_owner))
+ return;
+ }
+
+ g_free (self->i_name_owner);
+ self->i_name_owner = g_strdup (name_owner);
+
+ if (!self->i_name_owner)
+ _LOGT ("D-Bus name for %s has no owner", NM_DBUS_SERVICE);
+ else
+ _LOGT ("D-Bus name for %s has owner %s", NM_DBUS_SERVICE, i_name_owner);
+
+ //XXX: initialize parts.
+
+ //XXX: notify caller about name owner changed.
+}
+
+static void
+i_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)
+{
+ const char *new_owner;
+
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) {
+ g_variant_get (parameters,
+ "(&s&s&s)",
+ NULL,
+ NULL,
+ &new_owner);
+ i_name_owner_changed (user_data, nm_str_not_empty (new_owner), FALSE);
+ }
+}
+
+static void
+i_name_owner_get_cb (const char *name_owner,
+ GError *error,
+ gpointer user_data)
+{
+ if ( name_owner
+ || !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ i_name_owner_changed (user_data, name_owner, TRUE);
+}
+
+/*****************************************************************************/
+
+NMDBusClient *
+nm_dbus_client_new (GDBusConnection *dbus_connection,
+ const NMDBusClientCallbackTable *callback_table,
+ gpointer callback_user_data)
+{
+ NMDBusClient *self;
+
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (dbus_connection), NULL);
+ g_return_val_if_fail (callback_table, NULL);
+
+ self = g_slice_new (NMDBusClient);
+
+ *self = (NMDBusClient) {
+ .callback_table = callback_table,
+ .callback_user_data = callback_user_data,
+ .dbus_connection = g_object_ref (dbus_connection),
+ .caller_context = g_main_context_ref_thread_default (),
+ .intern_context = g_main_context_new (),
+ .event_lst_head = C_LIST_INIT (self->event_lst_head),
+ };
+
+ g_mutex_init (&self->event_lock);
+
+ g_main_context_push_thread_default (self->intern_context);
+
+ self->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (self->dbus_connection,
+ NM_DBUS_SERVICE,
+ i_name_owner_changed_cb,
+ self,
+ NULL);
+
+ self->i_name_owner_get_cancellable = g_cancellable_new ();
+ nm_dbus_connection_call_get_name_owner (self->dbus_connection,
+ NM_DBUS_SERVICE,
+ -1,
+ self->i_name_owner_get_cancellable,
+ i_name_owner_get_cb,
+ self);
+
+ g_main_context_pop_thread_default (self->intern_context);
+
+ return self;
+}
+
+void
+nm_dbus_client_free (NMDBusClient *self)
+{
+ g_return_if_fail (NM_IS_DBUS_CLIENT (self));
+
+ //XXX: handle pending events in event_lst_head
+ nm_assert (c_list_is_empty (&self->event_lst_head));
+
+ nm_clear_g_cancellable (&self->i_name_owner_get_cancellable);
+
+ nm_clear_g_dbus_connection_signal (self->dbus_connection,
+ &self->name_owner_changed_id);
+
+ g_object_unref (self->dbus_connection);
+
+ g_main_context_unref (self->caller_context);
+
+ //XXX: ensure there are no pending jobs (or we handle them correctly).
+ g_main_context_unref (self->intern_context);
+
+ g_mutex_clear (&self->event_lock);
+
+ nm_g_slice_free (self);
+}
diff --git a/libnm/nm-dbus-client.h b/libnm/nm-dbus-client.h
new file mode 100644
index 0000000000..542744f68c
--- /dev/null
+++ b/libnm/nm-dbus-client.h
@@ -0,0 +1,37 @@
+/*
+ * 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 2019 Red Hat, Inc.
+ */
+
+#ifndef __NM_DBUS_CLIENT_H__
+#define __NM_DBUS_CLIENT_H__
+
+typedef struct _NMDBusClient NMDBusClient;
+
+typedef struct {
+ void (*name_owner_changed) (NMDBusClient *self,
+ const char *new_owner,
+ gpointer user_data);
+} NMDBusClientCallbackTable;
+
+NMDBusClient *nm_dbus_client_new (GDBusConnection *dbus_connection,
+ const NMDBusClientCallbackTable *callback_table,
+ gpointer callback_user_data);
+
+void nm_dbus_client_free (NMDBusClient *self);
+
+#endif /* __NM_DBUS_CLIENT_H__ */