about summary refs log tree commit diff
path: root/src/dns
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns')
-rw-r--r--src/dns/nm-dns-manager.c11
-rw-r--r--src/dns/nm-dns-systemd-resolved.c283
-rw-r--r--src/dns/nm-dns-systemd-resolved.h2
3 files changed, 231 insertions, 65 deletions
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index c7c561c4..27c3e710 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -347,13 +347,20 @@ gboolean
 nm_dns_manager_has_systemd_resolved (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv;
+	NMDnsSystemdResolved *plugin = NULL;
 
 	g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE);
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
-	return    priv->sd_resolve_plugin
-	       || NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin);
+	if (priv->sd_resolve_plugin) {
+		nm_assert (!NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin));
+		plugin = NM_DNS_SYSTEMD_RESOLVED (priv->sd_resolve_plugin);
+	} else if (NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin))
+		plugin = NM_DNS_SYSTEMD_RESOLVED (priv->plugin);
+
+	return    plugin
+	       && nm_dns_systemd_resolved_is_running (plugin);
 }
 
 /*****************************************************************************/
diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 10797b86..5d262ba3 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -31,7 +31,7 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
-#include "nm-utils/nm-c-list.h"
+#include "nm-glib-aux/nm-c-list.h"
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
 #include "nm-utils.h"
@@ -42,9 +42,11 @@
 #include "nm-setting-connection.h"
 #include "devices/nm-device.h"
 #include "NetworkManagerUtils.h"
+#include "nm-std-aux/nm-dbus-compat.h"
 
-#define SYSTEMD_RESOLVED_DBUS_SERVICE "org.freedesktop.resolve1"
-#define SYSTEMD_RESOLVED_DBUS_PATH "/org/freedesktop/resolve1"
+#define SYSTEMD_RESOLVED_DBUS_SERVICE   "org.freedesktop.resolve1"
+#define SYSTEMD_RESOLVED_MANAGER_IFACE  "org.freedesktop.resolve1.Manager"
+#define SYSTEMD_RESOLVED_DBUS_PATH      "/org/freedesktop/resolve1"
 
 /*****************************************************************************/
 
@@ -62,10 +64,14 @@ typedef struct {
 /*****************************************************************************/
 
 typedef struct {
-	GDBusProxy *resolve;
-	GCancellable *init_cancellable;
-	GCancellable *update_cancellable;
+	GDBusConnection *dbus_connection;
+	GCancellable *cancellable;
 	CList request_queue_lst_head;
+	guint name_owner_changed_id;
+	bool send_updates_warn_ratelimited:1;
+	bool try_start_blocked:1;
+	bool dbus_has_owner:1;
+	bool dbus_initied:1;
 } NMDnsSystemdResolvedPrivate;
 
 struct _NMDnsSystemdResolved {
@@ -121,17 +127,26 @@ _interface_config_free (InterfaceConfig *config)
 static void
 call_done (GObject *source, GAsyncResult *r, gpointer user_data)
 {
-	GVariant *v;
-	GError *error = NULL;
+	gs_unref_variant GVariant *v = NULL;
+	gs_free_error GError *error = NULL;
 	NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data;
+	NMDnsSystemdResolvedPrivate *priv;
+
+	v = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), r, &error);
+	if (   !v
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
 
-	v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error);
 	if (!v) {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			return;
-		_LOGW ("Failed: %s", error->message);
-		g_error_free (error);
-	}
+		if (!priv->send_updates_warn_ratelimited) {
+			priv->send_updates_warn_ratelimited = TRUE;
+			_LOGW ("send-updates failed to update systemd-resolved: %s", error->message);
+		} else
+			_LOGD ("send-updates failed: %s", error->message);
+	} else
+		priv->send_updates_warn_ratelimited = FALSE;
 }
 
 static void
@@ -175,12 +190,11 @@ static void
 free_pending_updates (NMDnsSystemdResolved *self)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	RequestItem *request_item, *request_item_safe;
+	RequestItem *request_item;
 
-	c_list_for_each_entry_safe (request_item,
-	                            request_item_safe,
-	                            &priv->request_queue_lst_head,
-	                            request_queue_lst)
+	while ((request_item = c_list_first_entry (&priv->request_queue_lst_head,
+	                                           RequestItem,
+	                                           request_queue_lst)))
 		_request_item_free (request_item);
 }
 
@@ -267,27 +281,74 @@ static void
 send_updates (NMDnsSystemdResolved *self)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	RequestItem *request_item, *request_item_safe;
+	RequestItem *request_item;
+
+	if (c_list_is_empty (&priv->request_queue_lst_head)) {
+		/* nothing to do. */
+		return;
+	}
+
+	if (!priv->dbus_initied) {
+		_LOGT ("send-updates: D-Bus connection not ready");
+		return;
+	}
 
-	nm_clear_g_cancellable (&priv->update_cancellable);
+	if (!priv->dbus_has_owner) {
+		if (priv->try_start_blocked) {
+			/* we have no name owner and we already tried poking the service to
+			 * autostart. */
+			_LOGT ("send-updates: no name owner");
+			return;
+		}
 
-	if (!priv->resolve)
+		_LOGT ("send-updates: no name owner. Try start service...");
+		priv->try_start_blocked = TRUE;
+
+		g_dbus_connection_call (priv->dbus_connection,
+		                        DBUS_SERVICE_DBUS,
+		                        DBUS_PATH_DBUS,
+		                        DBUS_INTERFACE_DBUS,
+		                        "StartServiceByName",
+		                        g_variant_new ("(su)", SYSTEMD_RESOLVED_DBUS_SERVICE, 0u),
+		                        G_VARIANT_TYPE ("(u)"),
+		                        G_DBUS_CALL_FLAGS_NONE,
+		                        -1,
+		                        NULL,
+		                        NULL,
+		                        NULL);
 		return;
+	}
 
-	priv->update_cancellable = g_cancellable_new ();
-
-	c_list_for_each_entry_safe (request_item,
-	                            request_item_safe,
-	                            &priv->request_queue_lst_head,
-	                            request_queue_lst) {
-		g_dbus_proxy_call (priv->resolve,
-		                   request_item->operation,
-		                   request_item->argument,
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   priv->update_cancellable,
-		                   call_done,
-		                   self);
+	_LOGT ("send-updates: start %lu requests",
+	       c_list_length (&priv->request_queue_lst_head));
+
+	nm_clear_g_cancellable (&priv->cancellable);
+
+	priv->cancellable = g_cancellable_new ();
+
+	while ((request_item = c_list_first_entry (&priv->request_queue_lst_head,
+	                                           RequestItem,
+	                                           request_queue_lst))) {
+		/* Above we explicitly call "StartServiceByName" trying to avoid D-Bus activating systmd-resolved
+		 * multiple times. There is still a race, were we might hit this line although actually
+		 * the service just quit this very moment. In that case, we would try to D-Bus activate the
+		 * service multiple times during each call (something we wanted to avoid).
+		 *
+		 * But this is hard to avoid, because we'd have to check the error failure to detect the reason
+		 * and retry. The race is not critical, because at worst it results in logging a warning
+		 * about failure to start systemd.resolved. */
+		g_dbus_connection_call (priv->dbus_connection,
+		                        SYSTEMD_RESOLVED_DBUS_SERVICE,
+		                        SYSTEMD_RESOLVED_DBUS_PATH,
+		                        SYSTEMD_RESOLVED_MANAGER_IFACE,
+		                        request_item->operation,
+		                        request_item->argument,
+		                        NULL,
+		                        G_DBUS_CALL_FLAGS_NONE,
+		                        -1,
+		                        priv->cancellable,
+		                        call_done,
+		                        self);
 		_request_item_free (request_item);
 	}
 }
@@ -361,28 +422,100 @@ get_name (NMDnsPlugin *plugin)
 /*****************************************************************************/
 
 static void
-resolved_proxy_created (GObject *source, GAsyncResult *r, gpointer user_data)
+name_owner_changed (NMDnsSystemdResolved *self,
+                    const char *owner)
 {
-	NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data;
+	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
+
+	owner = nm_str_not_empty (owner);
+
+	if (!owner)
+		_LOGT ("D-Bus name for systemd-resolved has no owner");
+	else
+		_LOGT ("D-Bus name for systemd-resolved has owner %s", owner);
+
+	priv->dbus_has_owner = !!owner;
+	if (owner)
+		priv->try_start_blocked = FALSE;
+
+	send_updates (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)
+{
+	NMDnsSystemdResolved *self = user_data;
+	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
+	const char *new_owner;
+
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
+		return;
+
+	g_variant_get (parameters,
+	               "(&s&s&s)",
+	               NULL,
+	               NULL,
+	               &new_owner);
+
+	if (!priv->dbus_initied) {
+		/* There was a race and we got a NameOwnerChanged signal before GetNameOwner
+		 * returns. */
+		priv->dbus_initied = TRUE;
+		nm_clear_g_cancellable (&priv->cancellable);
+	}
+
+	name_owner_changed (user_data, new_owner);
+}
+
+static void
+get_name_owner_cb (GObject *source,
+                   GAsyncResult *res,
+                   gpointer user_data)
+{
+	NMDnsSystemdResolved *self;
 	NMDnsSystemdResolvedPrivate *priv;
+	gs_unref_variant GVariant *ret = NULL;
 	gs_free_error GError *error = NULL;
-	GDBusProxy *resolve;
+	const char *owner = NULL;
 
-	resolve = g_dbus_proxy_new_finish (r, &error);
-	if (   !resolve
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+	if (   !ret
 	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
+	if (ret)
+		g_variant_get (ret, "(&s)", &owner);
+
+	self = user_data;
 	priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	g_clear_object (&priv->init_cancellable);
-	if (!resolve) {
-		_LOGW ("failed to connect to resolved via DBus: %s", error->message);
-		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
-		return;
-	}
 
-	priv->resolve = resolve;
-	send_updates (self);
+	g_clear_object (&priv->cancellable);
+
+	priv->dbus_initied = TRUE;
+
+	name_owner_changed (self, owner);
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_dns_systemd_resolved_is_running (NMDnsSystemdResolved *self)
+{
+	NMDnsSystemdResolvedPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DNS_SYSTEMD_RESOLVED (self), FALSE);
+
+	priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
+
+	return    priv->dbus_initied
+	       && (   priv->dbus_has_owner
+	           || !priv->try_start_blocked);
 }
 
 /*****************************************************************************/
@@ -394,17 +527,35 @@ nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self)
 
 	c_list_init (&priv->request_queue_lst_head);
 
-	priv->init_cancellable = g_cancellable_new ();
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                          G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                          NULL,
-	                          SYSTEMD_RESOLVED_DBUS_SERVICE,
-	                          SYSTEMD_RESOLVED_DBUS_PATH,
-	                          SYSTEMD_RESOLVED_DBUS_SERVICE ".Manager",
-	                          priv->init_cancellable,
-	                          resolved_proxy_created,
-	                          self);
+	priv->dbus_connection = nm_g_object_ref (nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ()));
+	if (!priv->dbus_connection) {
+		_LOGD ("no D-Bus connection");
+		return;
+	}
+
+	priv->name_owner_changed_id = g_dbus_connection_signal_subscribe (priv->dbus_connection,
+	                                                                  DBUS_SERVICE_DBUS,
+	                                                                  DBUS_INTERFACE_DBUS,
+	                                                                  "NameOwnerChanged",
+	                                                                  DBUS_PATH_DBUS,
+	                                                                  SYSTEMD_RESOLVED_DBUS_SERVICE,
+	                                                                  G_DBUS_SIGNAL_FLAGS_NONE,
+	                                                                  name_owner_changed_cb,
+	                                                                  self,
+	                                                                  NULL);
+	priv->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)", SYSTEMD_RESOLVED_DBUS_SERVICE),
+	                        G_VARIANT_TYPE ("(s)"),
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        -1,
+	                        priv->cancellable,
+	                        get_name_owner_cb,
+	                        self);
 }
 
 NMDnsPlugin *
@@ -420,9 +571,15 @@ dispose (GObject *object)
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
 
 	free_pending_updates (self);
-	g_clear_object (&priv->resolve);
-	nm_clear_g_cancellable (&priv->init_cancellable);
-	nm_clear_g_cancellable (&priv->update_cancellable);
+
+	if (priv->name_owner_changed_id != 0) {
+		g_dbus_connection_signal_unsubscribe (priv->dbus_connection,
+		                                      nm_steal_int (&priv->name_owner_changed_id));
+	}
+
+	nm_clear_g_cancellable (&priv->cancellable);
+
+	g_clear_object (&priv->dbus_connection);
 
 	G_OBJECT_CLASS (nm_dns_systemd_resolved_parent_class)->dispose (object);
 }
diff --git a/src/dns/nm-dns-systemd-resolved.h b/src/dns/nm-dns-systemd-resolved.h
index 800a60c1..b79ff5e4 100644
--- a/src/dns/nm-dns-systemd-resolved.h
+++ b/src/dns/nm-dns-systemd-resolved.h
@@ -36,4 +36,6 @@ GType nm_dns_systemd_resolved_get_type (void);
 
 NMDnsPlugin *nm_dns_systemd_resolved_new (void);
 
+gboolean nm_dns_systemd_resolved_is_running (NMDnsSystemdResolved *self);
+
 #endif /* __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ */