summary refs log tree commit diff
path: root/src/nm-pacrunner-manager.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-07-31 10:51:42 +0200
committerMichael Biebl <biebl@debian.org>2019-07-31 10:51:42 +0200
commit2e5fa45ddfbb5cffa1e78221f1cea706e2f298af (patch)
tree86f69d36c56de3074280456eddc854a780b8e04b /src/nm-pacrunner-manager.c
parent85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff)
New upstream version 1.19.90 upstream/1.19.90
Diffstat (limited to 'src/nm-pacrunner-manager.c')
-rw-r--r--src/nm-pacrunner-manager.c779
1 files changed, 417 insertions, 362 deletions
diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c
index b9881f76..38b53eab 100644
--- a/src/nm-pacrunner-manager.c
+++ b/src/nm-pacrunner-manager.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -23,11 +22,14 @@
 #include "nm-pacrunner-manager.h"
 
 #include "nm-utils.h"
+#include "NetworkManagerUtils.h"
 #include "platform/nm-platform.h"
+#include "nm-dbus-manager.h"
 #include "nm-proxy-config.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "c-list/src/c-list.h"
+#include "nm-glib-aux/nm-dbus-aux.h"
 
 #define PACRUNNER_DBUS_SERVICE "org.pacrunner"
 #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
@@ -35,25 +37,27 @@
 
 /*****************************************************************************/
 
-struct _NMPacrunnerCallId {
-	CList lst;
+struct _NMPacrunnerConfId {
+	CList conf_id_lst;
 
-	/* this might be a dangling pointer after the async operation
-	 * is cancelled. */
-	NMPacrunnerManager *manager_maybe_dangling;
+	NMPacrunnerManager *self;
+
+	GVariant *parameters;
 
-	GVariant *args;
 	char *path;
+	guint64 log_id;
 	guint refcount;
 };
 
-typedef struct _NMPacrunnerCallId Config;
-
 typedef struct {
-	char *iface;
-	GDBusProxy *pacrunner;
+	GDBusConnection *dbus_connection;
 	GCancellable *cancellable;
-	CList configs;
+	CList conf_id_lst_head;
+	guint64 log_id_counter;
+	guint name_owner_changed_id;
+	bool dbus_initied:1;
+	bool has_name_owner:1;
+	bool try_start_blocked:1;
 } NMPacrunnerManagerPrivate;
 
 struct _NMPacrunnerManager {
@@ -79,469 +83,511 @@ NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYP
 #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__)
 
 #define _NMLOG2_PREFIX_NAME "pacrunner"
-#define _NMLOG2(level, config, ...) \
+#define _NMLOG2(level, conf_id, ...) \
 	G_STMT_START { \
 		nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
-		        "%s%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+		        "%s%"G_GUINT64_FORMAT"]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
 		        _NMLOG2_PREFIX_NAME": call[", \
-		        (config) \
-		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+		        (conf_id)->log_id \
+		        _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
 	} G_STMT_END
 
 /*****************************************************************************/
 
-static void pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data);
+static void _call_destroy_proxy_configuration (NMPacrunnerManager *self,
+                                               NMPacrunnerConfId *conf_id,
+                                               const char *path,
+                                               gboolean verbose_log);
 
 /*****************************************************************************/
 
-static Config *
-config_new (NMPacrunnerManager *manager, GVariant *args)
+static NMPacrunnerConfId *
+conf_id_ref (NMPacrunnerConfId *conf_id)
 {
-	Config *config;
-
-	config = g_slice_new0 (Config);
-	config->manager_maybe_dangling = manager;
-	config->args = g_variant_ref_sink (args);
-	config->refcount = 1;
-	c_list_link_tail (&NM_PACRUNNER_MANAGER_GET_PRIVATE (manager)->configs,
-	                  &config->lst);
+	nm_assert (conf_id);
+	nm_assert (conf_id->refcount > 0);
 
-	return config;
-}
-
-static Config *
-config_ref (Config *config)
-{
-	nm_assert (config);
-	nm_assert (config->refcount > 0);
-
-	config->refcount++;
-	return config;
+	conf_id->refcount++;
+	return conf_id;
 }
 
 static void
-config_unref (Config *config)
+conf_id_unref (NMPacrunnerConfId *conf_id)
 {
-	nm_assert (config);
-	nm_assert (config->refcount > 0);
-
-	if (config->refcount == 1) {
-		g_variant_unref (config->args);
-		g_free (config->path);
-		c_list_unlink_stale (&config->lst);
-		g_slice_free (Config, config);
+	nm_assert (conf_id);
+	nm_assert (conf_id->refcount > 0);
+
+	if (conf_id->refcount == 1) {
+		g_variant_unref (conf_id->parameters);
+		g_free (conf_id->path);
+		c_list_unlink_stale (&conf_id->conf_id_lst);
+		g_object_unref (conf_id->self);
+		g_slice_free (NMPacrunnerConfId, conf_id);
 	} else
-		config->refcount--;
+		conf_id->refcount--;
 }
 
+NM_AUTO_DEFINE_FCN0 (NMPacrunnerConfId *, _nm_auto_unref_conf_id, conf_id_unref);
+#define nm_auto_unref_conf_id nm_auto (_nm_auto_unref_conf_id)
+
 /*****************************************************************************/
 
 static void
-add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config)
+get_ip_domains (GPtrArray *domains, NMIPConfig *ip_config)
 {
-	const char *pac_url, *pac_script;
+	NMDedupMultiIter ipconf_iter;
+	char *cidr;
+	guint i, num;
+	char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+	int addr_family;
+
+	if (!ip_config)
+		return;
+
+	addr_family = nm_ip_config_get_addr_family (ip_config);
+
+	num = nm_ip_config_get_num_searches (ip_config);
+	for (i = 0; i < num; i++)
+		g_ptr_array_add (domains, g_strdup (nm_ip_config_get_search (ip_config, i)));
+
+	num = nm_ip_config_get_num_domains (ip_config);
+	for (i = 0; i < num; i++)
+		g_ptr_array_add (domains, g_strdup (nm_ip_config_get_domain (ip_config, i)));
+
+	if (addr_family == AF_INET) {
+		const NMPlatformIP4Address *address;
+
+		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, (NMIP4Config *) ip_config, &address) {
+			cidr = g_strdup_printf ("%s/%u",
+			                        nm_utils_inet4_ntop (address->address, sbuf),
+			                        address->plen);
+			g_ptr_array_add (domains, cidr);
+		}
+	} else {
+		const NMPlatformIP6Address *address;
+
+		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, (NMIP6Config *) ip_config, &address) {
+			cidr = g_strdup_printf ("%s/%u",
+			                        nm_utils_inet6_ntop (&address->address, sbuf),
+			                        address->plen);
+			g_ptr_array_add (domains, cidr);
+		}
+	}
+
+	if (addr_family == AF_INET) {
+		const NMPlatformIP4Route *routes;
+
+		nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, (NMIP4Config *) ip_config, &routes) {
+			if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes))
+				continue;
+			cidr = g_strdup_printf ("%s/%u",
+			                        nm_utils_inet4_ntop (routes->network, sbuf),
+			                        routes->plen);
+			g_ptr_array_add (domains, cidr);
+		}
+	} else {
+		const NMPlatformIP6Route *routes;
+
+		nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, (NMIP6Config *) ip_config, &routes) {
+			if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes))
+				continue;
+			cidr = g_strdup_printf ("%s/%u",
+			                        nm_utils_inet6_ntop (&routes->network, sbuf),
+			                        routes->plen);
+			g_ptr_array_add (domains, cidr);
+		}
+	}
+}
+
+static GVariant *
+_make_request_create_proxy_configuration (NMProxyConfig *proxy_config,
+                                          const char *iface,
+                                          NMIP4Config *ip4_config,
+                                          NMIP6Config *ip6_config)
+{
+	GVariantBuilder builder;
 	NMProxyConfigMethod method;
+	const char *pac_url;
+	const char *pac_script;
+
+	nm_assert (NM_IS_PROXY_CONFIG (proxy_config));
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+
+	if (iface) {
+		g_variant_builder_add (&builder, "{sv}",
+		                       "Interface",
+		                       g_variant_new_string (iface));
+	}
 
 	method = nm_proxy_config_get_method (proxy_config);
+	switch (method) {
+	case NM_PROXY_CONFIG_METHOD_AUTO:
+		g_variant_builder_add (&builder, "{sv}",
+		                       "Method",
+		                       g_variant_new_string ("auto"));
 
-	if (method == NM_PROXY_CONFIG_METHOD_AUTO) {
 		pac_url = nm_proxy_config_get_pac_url (proxy_config);
 		if (pac_url) {
-			g_variant_builder_add (proxy_data, "{sv}",
+			g_variant_builder_add (&builder, "{sv}",
 			                       "URL",
 			                       g_variant_new_string (pac_url));
 		}
 
 		pac_script = nm_proxy_config_get_pac_script (proxy_config);
 		if (pac_script) {
-			g_variant_builder_add (proxy_data, "{sv}",
+			g_variant_builder_add (&builder, "{sv}",
 			                       "Script",
 			                       g_variant_new_string (pac_script));
 		}
+		break;
+	case NM_PROXY_CONFIG_METHOD_NONE:
+		g_variant_builder_add (&builder, "{sv}",
+		                       "Method",
+		                       g_variant_new_string ("direct"));
+		break;
 	}
 
-	g_variant_builder_add (proxy_data, "{sv}",
+	g_variant_builder_add (&builder, "{sv}",
 	                       "BrowserOnly",
 	                       g_variant_new_boolean (nm_proxy_config_get_browser_only (proxy_config)));
-}
-
-static void
-get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4)
-{
-	NMDedupMultiIter ipconf_iter;
-	char *cidr;
-	const NMPlatformIP4Address *address;
-	const NMPlatformIP4Route *routes;
-	guint i;
-	char sbuf[NM_UTILS_INET_ADDRSTRLEN];
 
-	/* Extract searches */
-	for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++)
-		g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_search (ip4, i)));
+	if (ip4_config || ip6_config) {
+		gs_unref_ptrarray GPtrArray *domains = NULL;
 
-	/* Extract domains */
-	for (i = 0; i < nm_ip4_config_get_num_domains (ip4); i++)
-		g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_domain (ip4, i)));
+		domains = g_ptr_array_new_with_free_func (g_free);
 
-	/* Add addresses and routes in CIDR form */
+		get_ip_domains (domains, NM_IP_CONFIG_CAST (ip4_config));
+		get_ip_domains (domains, NM_IP_CONFIG_CAST (ip6_config));
 
-	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) {
-		cidr = g_strdup_printf ("%s/%u",
-		                        nm_utils_inet4_ntop (address->address, sbuf),
-		                        address->plen);
-		g_ptr_array_add (domains, cidr);
+		if (domains->len > 0) {
+			g_variant_builder_add (&builder, "{sv}",
+			                       "Domains",
+			                       g_variant_new_strv ((const char *const*) domains->pdata,
+			                                           domains->len));
+		}
 	}
 
-	nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &routes) {
-		if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes))
-			continue;
-		cidr = g_strdup_printf ("%s/%u",
-		                        nm_utils_inet4_ntop (routes->network, sbuf),
-		                        routes->plen);
-		g_ptr_array_add (domains, cidr);
-	}
+	return g_variant_new ("(a{sv})", &builder);
 }
 
+/*****************************************************************************/
+
 static void
-get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6)
+_call_destroy_proxy_configuration_cb (GObject *source,
+                                      GAsyncResult *res,
+                                      gpointer user_data)
 {
-	NMDedupMultiIter ipconf_iter;
-	char *cidr;
-	const NMPlatformIP6Address *address;
-	const NMPlatformIP6Route *routes;
-	guint i;
-	char sbuf[NM_UTILS_INET_ADDRSTRLEN];
-
-	/* Extract searches */
-	for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++)
-		g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_search (ip6, i)));
-
-	/* Extract domains */
-	for (i = 0; i < nm_ip6_config_get_num_domains (ip6); i++)
-		g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_domain (ip6, i)));
-
-	/* Add addresses and routes in CIDR form */
-	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) {
-		cidr = g_strdup_printf ("%s/%u",
-		                        nm_utils_inet6_ntop (&address->address, sbuf),
-		                        address->plen);
-		g_ptr_array_add (domains, cidr);
-	}
+	nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *ret = NULL;
 
-	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &routes) {
-		if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes))
-			continue;
-		cidr = g_strdup_printf ("%s/%u",
-		                        nm_utils_inet6_ntop (&routes->network, sbuf),
-		                        routes->plen);
-		g_ptr_array_add (domains, cidr);
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+	if (!ret) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			_LOG2T (conf_id, "destroy proxy configuration: failed with %s", error->message);
+		else
+			_LOG2T (conf_id, "destroy proxy configuration: cancelled");
+		return;
 	}
-}
-
-/*****************************************************************************/
-
-static GCancellable *
-_ensure_cancellable (NMPacrunnerManagerPrivate *priv)
-{
-	if (G_UNLIKELY (!priv->cancellable))
-		priv->cancellable = g_cancellable_new ();
-	return priv->cancellable;
+	_LOG2T (conf_id, "destroy proxy configuration: success");
 }
 
 static void
-pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data)
+_call_create_proxy_configuration_cb (GObject *source,
+                                     GAsyncResult *res,
+                                     gpointer user_data)
 {
-	Config *config = user_data;
-	NMPacrunnerManager *self;
-	NMPacrunnerManagerPrivate *priv;
+	nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data;
+	NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (conf_id->self);
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *variant = NULL;
 	const char *path = NULL;
 
-	nm_assert (!config->path);
+	nm_assert (!conf_id->path);
 
-	variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		goto out;
+	variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
 
-	self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling);
-	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
-
-	if (!variant)
-		_LOG2D (config, "sending failed: %s", error->message);
-	else {
-		g_variant_get (variant, "(&o)", &path);
-
-		if (c_list_is_empty (&config->lst)) {
-			_LOG2D (config, "sent (%s), but destroy it right away", path);
-			g_dbus_proxy_call (priv->pacrunner,
-			                   "DestroyProxyConfiguration",
-			                   g_variant_new ("(o)", path),
-			                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
-			                   -1,
-			                   _ensure_cancellable (priv),
-			                   pacrunner_remove_done,
-			                   config_ref (config));
-		} else {
-			_LOG2D (config, "sent (%s)", path);
-			config->path = g_strdup (path);
-		}
+	if (!variant) {
+		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			_LOG2T (conf_id, "create proxy configuration failed: %s", error->message);
+		else
+			_LOG2T (conf_id, "create proxy configuration cancelled");
+		return;
 	}
 
-out:
-	config_unref (config);
+	g_variant_get (variant, "(&o)", &path);
+
+	if (c_list_is_empty (&conf_id->conf_id_lst)) {
+		_LOG2T (conf_id, "create proxy configuration succeeded (%s), but destroy it right away", path);
+		_call_destroy_proxy_configuration (self,
+		                                   conf_id,
+		                                   path,
+		                                   FALSE);
+	} else {
+		_LOG2T (conf_id, "create proxy configuration succeeded (%s)", path);
+		conf_id->path = g_strdup (path);
+	}
 }
 
 static void
-pacrunner_send_config (NMPacrunnerManager *self, Config *config)
+_call_destroy_proxy_configuration (NMPacrunnerManager *self,
+                                   NMPacrunnerConfId *conf_id,
+                                   const char *path,
+                                   gboolean verbose_log)
 {
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-	if (priv->pacrunner) {
-		_LOG2T (config, "sending...");
-
-		nm_assert (!config->path);
-		g_dbus_proxy_call (priv->pacrunner,
-		                   "CreateProxyConfiguration",
-		                   config->args,
-		                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
-		                   -1,
-		                   _ensure_cancellable (priv),
-		                   pacrunner_send_done,
-		                   config_ref (config));
-	}
+	if (verbose_log)
+		_LOG2T (conf_id, "destroy proxy configuration %s...", path);
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        PACRUNNER_DBUS_SERVICE,
+	                        PACRUNNER_DBUS_PATH,
+	                        PACRUNNER_DBUS_INTERFACE,
+	                        "DestroyProxyConfiguration",
+	                        g_variant_new ("(o)", path),
+	                        G_VARIANT_TYPE ("()"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        NM_SHUTDOWN_TIMEOUT_MS,
+	                        priv->cancellable,
+	                        _call_destroy_proxy_configuration_cb,
+	                        conf_id_ref (conf_id));
 }
 
 static void
-name_owner_changed (NMPacrunnerManager *self)
+_call_create_proxy_configuration (NMPacrunnerManager *self,
+                                  NMPacrunnerConfId *conf_id,
+                                  gboolean verbose_log)
 {
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
-	gs_free char *owner = NULL;
-	CList *iter;
-
-	owner = g_dbus_proxy_get_name_owner (priv->pacrunner);
-	if (owner) {
-		_LOGD ("name owner appeared (%s)", owner);
-		c_list_for_each (iter, &priv->configs)
-			pacrunner_send_config (self, c_list_entry (iter, Config, lst));
-	} else {
-		_LOGD ("name owner disappeared");
-		nm_clear_g_cancellable (&priv->cancellable);
-		c_list_for_each (iter, &priv->configs)
-			nm_clear_g_free (&c_list_entry (iter, Config, lst)->path);
-	}
-}
 
-static void
-name_owner_changed_cb (GObject *object,
-                       GParamSpec *pspec,
-                       gpointer user_data)
-{
-	name_owner_changed (user_data);
+	if (verbose_log)
+		_LOG2T (conf_id, "create proxy configuration...");
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        PACRUNNER_DBUS_SERVICE,
+	                        PACRUNNER_DBUS_PATH,
+	                        PACRUNNER_DBUS_INTERFACE,
+	                        "CreateProxyConfiguration",
+	                        conf_id->parameters,
+	                        G_VARIANT_TYPE ("(o)"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        NM_SHUTDOWN_TIMEOUT_MS,
+	                        priv->cancellable,
+	                        _call_create_proxy_configuration_cb,
+	                        conf_id_ref (conf_id));
 }
 
-static void
-pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+static gboolean
+_try_start_service_by_name (NMPacrunnerManager *self)
 {
-	NMPacrunnerManager *self = user_data;
-	NMPacrunnerManagerPrivate *priv;
-	gs_free_error GError *error = NULL;
-	GDBusProxy *proxy;
-
-	proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-	if (!proxy) {
-		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			_LOGE ("failed to create D-Bus proxy for pacrunner: %s", error->message);
-		return;
-	}
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	if (   priv->try_start_blocked
+	    || !priv->dbus_initied)
+		return FALSE;
 
-	priv->pacrunner = proxy;
-	g_signal_connect (priv->pacrunner, "notify::g-name-owner",
-	                  G_CALLBACK (name_owner_changed_cb), self);
-	name_owner_changed (self);
+	_LOGD ("try D-Bus activating pacrunner...");
+	priv->try_start_blocked = TRUE;
+	nm_dbus_connection_call_start_service_by_name (priv->dbus_connection,
+	                                               PACRUNNER_DBUS_SERVICE,
+	                                               -1,
+	                                               NULL,
+	                                               NULL,
+	                                               NULL);
+	return TRUE;
 }
 
+/*****************************************************************************/
+
 /**
- * nm_pacrunner_manager_send:
+ * nm_pacrunner_manager_add:
  * @self: the #NMPacrunnerManager
- * @iface: the iface for the connection or %NULL
  * @proxy_config: proxy config of the connection
+ * @iface: the iface for the connection or %NULL
  * @ip4_config: IP4 config of the connection to extract domain info from
  * @ip6_config: IP6 config of the connection to extract domain info from
  *
- * Returns: a #NMPacrunnerCallId call id. The function cannot
- *  fail and always returns a non NULL pointer. The call-id may
+ * Returns: a #NMPacrunnerConfId id. The function cannot
+ *  fail and always returns a non NULL pointer. The conf-id may
  *  be used to remove the configuration later via nm_pacrunner_manager_remove().
- *  Note that the call-id does not keep the @self instance alive.
- *  If you plan to remove the configuration later, you must keep
- *  the instance alive long enough. You can remove the configuration
- *  at most once using this call call-id.
+ *  Note that the conf-id keeps the @self instance alive.
  */
-NMPacrunnerCallId *
-nm_pacrunner_manager_send (NMPacrunnerManager *self,
-                           const char *iface,
-                           NMProxyConfig *proxy_config,
-                           NMIP4Config *ip4_config,
-                           NMIP6Config *ip6_config)
+NMPacrunnerConfId *
+nm_pacrunner_manager_add (NMPacrunnerManager *self,
+                          NMProxyConfig *proxy_config,
+                          const char *iface,
+                          NMIP4Config *ip4_config,
+                          NMIP6Config *ip6_config)
 {
-	char **strv = NULL;
-	NMProxyConfigMethod method;
 	NMPacrunnerManagerPrivate *priv;
-	GVariantBuilder proxy_data;
-	GPtrArray *domains;
-	Config *config;
+	NMPacrunnerConfId *conf_id;
+	gs_free char *log_msg = NULL;
 
 	g_return_val_if_fail (NM_IS_PACRUNNER_MANAGER (self), NULL);
 	g_return_val_if_fail (proxy_config, NULL);
 
 	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-	g_free (priv->iface);
-	priv->iface = g_strdup (iface);
-
-	g_variant_builder_init (&proxy_data, G_VARIANT_TYPE_VARDICT);
-
-	if (iface) {
-		g_variant_builder_add (&proxy_data, "{sv}",
-		                       "Interface",
-		                       g_variant_new_string (iface));
+	conf_id = g_slice_new (NMPacrunnerConfId);
+	*conf_id = (NMPacrunnerConfId) {
+		.log_id     = ++priv->log_id_counter,
+		.refcount   = 1,
+		.self       = g_object_ref (self),
+		.parameters = g_variant_ref_sink (_make_request_create_proxy_configuration (proxy_config,
+		                                                                            iface,
+		                                                                            ip4_config,
+		                                                                            ip6_config)),
+	};
+	c_list_link_tail (&priv->conf_id_lst_head,
+	                  &conf_id->conf_id_lst);
+
+	if (!priv->has_name_owner) {
+		_LOG2T (conf_id, "add config: %s (%s)",
+		        (log_msg = g_variant_print (conf_id->parameters, FALSE)),
+		        "pacrunner D-Bus service not running");
+		_try_start_service_by_name (self);
+	} else {
+		_LOG2T (conf_id, "add config: %s (%s)",
+		        (log_msg = g_variant_print (conf_id->parameters, FALSE)),
+		        "create proxy configuration");
+		_call_create_proxy_configuration (self, conf_id, FALSE);
 	}
 
-	method = nm_proxy_config_get_method (proxy_config);
-	switch (method) {
-	case NM_PROXY_CONFIG_METHOD_AUTO:
-		g_variant_builder_add (&proxy_data, "{sv}",
-		                       "Method",
-		                       g_variant_new_string ("auto"));
+	return conf_id;
+}
 
-		break;
-	case NM_PROXY_CONFIG_METHOD_NONE:
-		g_variant_builder_add (&proxy_data, "{sv}",
-		                       "Method",
-		                       g_variant_new_string ("direct"));
-	}
+/**
+ * nm_pacrunner_manager_remove:
+ * @conf_id: the conf id obtained from nm_pacrunner_manager_add()
+ */
+void
+nm_pacrunner_manager_remove (NMPacrunnerConfId *conf_id)
+{
+	_nm_unused nm_auto_unref_conf_id NMPacrunnerConfId *conf_id_free = conf_id;
+	NMPacrunnerManager *self;
+	NMPacrunnerManagerPrivate *priv;
 
-	/* Extract stuff from configs */
-	add_proxy_config (&proxy_data, proxy_config);
+	g_return_if_fail (conf_id);
 
-	if (ip4_config || ip6_config) {
-		domains = g_ptr_array_new_with_free_func (g_free);
+	self = conf_id->self;
 
-		if (ip4_config)
-			get_ip4_domains (domains, ip4_config);
-		if (ip6_config)
-			get_ip6_domains (domains, ip6_config);
+	g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self));
 
-		g_ptr_array_add (domains, NULL);
-		strv = (char **) g_ptr_array_free (domains, (domains->len == 1));
+	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-		if (strv) {
-			g_variant_builder_add (&proxy_data, "{sv}",
-			                       "Domains",
-			                       g_variant_new_strv ((const char *const *) strv, -1));
-			g_strfreev (strv);
-		}
+	_LOG2T (conf_id, "removing...");
+
+	nm_assert (c_list_contains (&priv->conf_id_lst_head, &conf_id->conf_id_lst));
+
+	c_list_unlink (&conf_id->conf_id_lst);
+
+	if (!conf_id->path) {
+		/* There is no ID to destroy the configuration.
+		 *
+		 * That can happen because:
+		 *
+		 *  - pacrunner D-Bus service is not running (no name owner) and we didn't call CreateProxyConfiguration.
+		 *  - CreateProxyConfiguration failed.
+		 *  - CreateProxyConfiguration is in progress.
+		 *
+		 * In all cases there is nothing to do. Note that if CreateProxyConfiguration is in progress
+		 * it has a reference on the conf-id and it will automatically destroy the configuration
+		 * when it completes.
+		 */
+		return;
 	}
 
-	config = config_new (self, g_variant_new ("(a{sv})", &proxy_data));
-
-	{
-		gs_free char *args_str = NULL;
-
-		_LOG2D (config, "send: new config %s",
-		        (args_str = g_variant_print (config->args, FALSE)));
-	}
+	_call_destroy_proxy_configuration (self, conf_id, conf_id->path, TRUE);
+}
 
-	/* Send if pacrunner is available on bus, otherwise
-	 * config has already been appended above to be
-	 * sent when pacrunner appears.
-	 */
-	pacrunner_send_config (self, config);
+gboolean
+nm_pacrunner_manager_remove_clear (NMPacrunnerConfId **p_conf_id)
+{
+	g_return_val_if_fail (p_conf_id, FALSE);
 
-	return config;
+	if (!*p_conf_id)
+		return FALSE;
+	nm_pacrunner_manager_remove (g_steal_pointer (p_conf_id));
+	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data)
+name_owner_changed (NMPacrunnerManager *self,
+                    const char *name_owner)
 {
-	Config *config = user_data;
-	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *ret = NULL;
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	NMPacrunnerConfId *conf_id;
+	gboolean has_name_owner;
 
-	ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
-	if (!ret) {
-		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			goto out;
-		_LOG2D (config, "remove failed: %s", error->message);
-		goto out;
-	}
+	has_name_owner = (name_owner && name_owner[0]);
+
+	if (   priv->dbus_initied
+	    && priv->has_name_owner == has_name_owner)
+		return;
+
+	priv->has_name_owner = has_name_owner;
 
-	_LOG2D (config, "removed");
+	nm_clear_g_cancellable (&priv->cancellable);
 
-out:
-	config_unref (config);
+	if (has_name_owner) {
+		priv->dbus_initied = TRUE;
+		priv->try_start_blocked = FALSE;
+		_LOGD ("pacrunner appeared on D-Bus (%s)", name_owner);
+		priv->cancellable = g_cancellable_new ();
+		c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst)
+			_call_create_proxy_configuration (self, conf_id, TRUE);
+	} else {
+		if (!priv->dbus_initied) {
+			priv->dbus_initied = TRUE;
+			nm_assert (!priv->try_start_blocked);
+			_LOGD ("pacrunner not on D-Bus");
+		} else
+			_LOGD ("pacrunner disappeared from D-Bus");
+		if (!c_list_is_empty (&priv->conf_id_lst_head)) {
+			c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst)
+				nm_clear_g_free (&conf_id->path);
+			_try_start_service_by_name (self);
+		}
+	}
 }
 
-/**
- * nm_pacrunner_manager_remove:
- * @self: the #NMPacrunnerManager
- * @call_id: the call-id obtained from nm_pacrunner_manager_send()
- */
-void
-nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_id)
+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)
 {
-	NMPacrunnerManagerPrivate *priv;
-	Config *config;
+	const char *new_owner;
 
-	g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self));
-	g_return_if_fail (call_id);
-
-	config = call_id;
-	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
+		return;
 
-	_LOG2T (config, "removing...");
-
-	nm_assert (c_list_contains (&priv->configs, &config->lst));
-
-	if (priv->pacrunner) {
-		if (!config->path) {
-			/* send() failed or is still pending. The item is unlinked from
-			 * priv->configs, so pacrunner_send_done() knows to call
-			 * DestroyProxyConfiguration right away.
-			 */
-		} else {
-			g_dbus_proxy_call (priv->pacrunner,
-			                   "DestroyProxyConfiguration",
-			                   g_variant_new ("(o)", config->path),
-			                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
-			                   -1,
-			                   _ensure_cancellable (priv),
-			                   pacrunner_remove_done,
-			                   config_ref (config));
-			nm_clear_g_free (&config->path);
-		}
-	}
+	g_variant_get (parameters,
+	               "(&s&s&s)",
+	               NULL,
+	               NULL,
+	               &new_owner);
 
-	c_list_unlink (&config->lst);
-	config_unref (config);
+	name_owner_changed (user_data, new_owner);
 }
 
-gboolean
-nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self,
-                                   NMPacrunnerCallId **p_call_id)
+static void
+get_name_owner_cb (const char *name_owner,
+                   GError *error,
+                   gpointer user_data)
 {
-	g_return_val_if_fail (p_call_id, FALSE);
-
-	/* if we have no call-id, allow for %NULL */
-	g_return_val_if_fail ((!self && !*p_call_id) || NM_IS_PACRUNNER_MANAGER (self), FALSE);
+	if (   !name_owner
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
 
-	if (!*p_call_id)
-		return FALSE;
-	nm_pacrunner_manager_remove (self,
-	                             g_steal_pointer (p_call_id));
-	return TRUE;
+	name_owner_changed (user_data, name_owner);
 }
 
 /*****************************************************************************/
@@ -551,28 +597,36 @@ nm_pacrunner_manager_init (NMPacrunnerManager *self)
 {
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-	c_list_init (&priv->configs);
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_NONE,
-	                          NULL,
-	                          PACRUNNER_DBUS_SERVICE,
-	                          PACRUNNER_DBUS_PATH,
-	                          PACRUNNER_DBUS_INTERFACE,
-	                          _ensure_cancellable (priv),
-	                          pacrunner_proxy_cb,
-	                          self);
+	c_list_init (&priv->conf_id_lst_head);
+
+	priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
+
+	if (!priv->dbus_connection) {
+		_LOGD ("no D-Bus connection to talk to pacrunner");
+		return;
+	}
+
+	priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+	                                                                                      PACRUNNER_DBUS_SERVICE,
+	                                                                                      name_owner_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+	priv->cancellable = g_cancellable_new ();
+
+	nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+	                                        PACRUNNER_DBUS_SERVICE,
+	                                        -1,
+	                                        priv->cancellable,
+	                                        get_name_owner_cb,
+	                                        self);
 }
 
 static void
 dispose (GObject *object)
 {
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object);
-	CList *iter, *safe;
 
-	c_list_for_each_safe (iter, safe, &priv->configs) {
-		c_list_unlink (iter);
-		config_unref (c_list_entry (iter, Config, lst));
-	}
+	nm_assert (c_list_is_empty (&priv->conf_id_lst_head));
 
 	/* we cancel all pending operations. Note that pacrunner automatically
 	 * removes all configuration once NetworkManager disconnects from
@@ -580,8 +634,9 @@ dispose (GObject *object)
 	 */
 	nm_clear_g_cancellable (&priv->cancellable);
 
-	g_clear_pointer (&priv->iface, g_free);
-	g_clear_object (&priv->pacrunner);
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
+	g_clear_object (&priv->dbus_connection);
 
 	G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object);
 }