about summary refs log tree commit diff
path: root/src/supplicant/nm-supplicant-manager.c
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2020-05-19 16:38:36 +0200
committerSebastien Bacher <seb128@ubuntu.com>2020-05-19 17:09:07 +0200
commitccf6dc06bbee82c3d49f451545c5317337e0777e (patch)
treea8fddc8c6e2b3b99bebab1d5bb2a64581eff4bfd /src/supplicant/nm-supplicant-manager.c
parentf109e55ef130ce84054d5ba3acf4b71cd8c7564a (diff)
parent7ffed1e6136de75188f10ba8763bcb942f932f8e (diff)
Merge remote-tracking branch 'salsa/debian/master' into ubuntu/master
Diffstat (limited to 'src/supplicant/nm-supplicant-manager.c')
-rw-r--r--src/supplicant/nm-supplicant-manager.c1528
1 files changed, 1143 insertions, 385 deletions
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c
index 49581f6e..b1a2dea8 100644
--- a/src/supplicant/nm-supplicant-manager.c
+++ b/src/supplicant/nm-supplicant-manager.c
@@ -8,29 +8,64 @@
 
 #include "nm-supplicant-manager.h"
 
+#include "nm-core-internal.h"
+#include "nm-dbus-manager.h"
+#include "nm-glib-aux/nm-dbus-aux.h"
+#include "nm-glib-aux/nm-ref-string.h"
 #include "nm-supplicant-interface.h"
 #include "nm-supplicant-types.h"
-#include "nm-core-internal.h"
+#include "platform/nm-platform.h"
 
 /*****************************************************************************/
 
+#define CREATE_IFACE_TRY_COUNT_MAX 7u
+
+struct _NMSupplMgrCreateIfaceHandle {
+	NMSupplicantManager *self;
+	CList create_iface_lst;
+	GCancellable *cancellable;
+	NMSupplicantManagerCreateInterfaceCb callback;
+	gpointer callback_user_data;
+	NMShutdownWaitObjHandle *shutdown_handle;
+	NMRefString *name_owner;
+	GError *fail_on_idle_error;
+	NMSupplicantDriver driver;
+	int ifindex;
+	guint fail_on_idle_id;
+	guint create_iface_try_count:5;
+};
+
+enum {
+	AVAILABLE_CHANGED,
+	LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 typedef struct {
-	GDBusProxy *     proxy;
-	GCancellable *   cancellable;
-	gboolean         running;
-
-	GSList          *ifaces;
-	NMSupplicantFeature fast_support;
-	NMSupplicantFeature ap_support;
-	NMSupplicantFeature pmf_support;
-	NMSupplicantFeature fils_support;
-	NMSupplicantFeature p2p_support;
-	NMSupplicantFeature mesh_support;
-	NMSupplicantFeature wfd_support;
-	NMSupplicantFeature ft_support;
-	NMSupplicantFeature sha384_support;
-	guint             die_count_reset_id;
-	guint             die_count;
+	GDBusConnection *dbus_connection;
+
+	NMRefString *name_owner;
+
+	GCancellable *get_name_owner_cancellable;
+	GCancellable *get_capabilities_cancellable;
+	GCancellable *poke_name_owner_cancellable;
+
+	GHashTable *supp_ifaces;
+	CList supp_lst_head;
+
+	CList create_iface_lst_head;
+
+	NMSupplCapMask capabilities;
+
+	guint name_owner_changed_id;
+	guint interface_removed_id;
+	guint poke_name_owner_timeout_id;
+	guint available_reset_id;
+
+	/* see nm_supplicant_manager_get_available(). */
+	NMTernary available:2;
+
 } NMSupplicantManagerPrivate;
 
 struct _NMSupplicantManager {
@@ -46,6 +81,8 @@ G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT)
 
 #define NM_SUPPLICANT_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSupplicantManager, NM_IS_SUPPLICANT_MANAGER)
 
+NM_DEFINE_SINGLETON_GETTER (NMSupplicantManager, nm_supplicant_manager_get, NM_TYPE_SUPPLICANT_MANAGER);
+
 /*****************************************************************************/
 
 #define _NMLOG_DOMAIN      LOGD_SUPPLICANT
@@ -57,72 +94,140 @@ NM_CACHED_QUARK_FCN ("nm-supplicant-error-quark", nm_supplicant_error_quark)
 
 /*****************************************************************************/
 
-static gboolean
-die_count_exceeded (guint32 count)
+static void _create_iface_proceed_all (NMSupplicantManager *self,
+                                       GError *error);
+static void _supp_iface_add (NMSupplicantManager *self,
+                             NMRefString *iface_path,
+                             NMSupplicantInterface *supp_iface);
+static void _supp_iface_remove_one (NMSupplicantManager *self,
+                                    NMSupplicantInterface *supp_iface,
+                                    gboolean force_remove_from_supplicant,
+                                    const char *reason);
+static void _create_iface_dbus_call_get_interface (NMSupplicantManager *self,
+                                                   NMSupplMgrCreateIfaceHandle *handle,
+                                                   const char *ifname);
+static void _create_iface_dbus_call_create_interface (NMSupplicantManager *self,
+                                                      NMSupplMgrCreateIfaceHandle *handle,
+                                                      const char *ifname);
+static gboolean _create_iface_fail_on_idle_cb (gpointer user_data);
+
+static gboolean _available_reset_cb (gpointer user_data);
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_driver_to_string, NMSupplicantDriver,
+	NM_UTILS_LOOKUP_DEFAULT_WARN (NULL),
+	NM_UTILS_LOOKUP_ITEM    (NM_SUPPLICANT_DRIVER_UNKNOWN,  "???"),
+	NM_UTILS_LOOKUP_ITEM    (NM_SUPPLICANT_DRIVER_WIRELESS, NM_WPAS_DEFAULT_WIFI_DRIVER),
+	NM_UTILS_LOOKUP_ITEM    (NM_SUPPLICANT_DRIVER_WIRED,    "wired"),
+	NM_UTILS_LOOKUP_ITEM    (NM_SUPPLICANT_DRIVER_MACSEC,   "macsec_linux"),
+);
+
+/*****************************************************************************/
+
+NMTernary
+nm_supplicant_manager_is_available (NMSupplicantManager *self)
 {
-	return count > 2;
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NM_TERNARY_FALSE);
+
+	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->available;
 }
 
-static gboolean
-is_available (NMSupplicantManager *self)
+NMRefString *
+nm_supplicant_manager_get_dbus_name_owner (NMSupplicantManager *self)
 {
-	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
 
-	return    priv->running
-	       && !die_count_exceeded (priv->die_count);
+	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->name_owner;
+}
+
+GDBusConnection *nm_supplicant_manager_get_dbus_connection (NMSupplicantManager *self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
+
+	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_connection;
+}
+
+NMSupplCapMask
+nm_supplicant_manager_get_global_capabilities (NMSupplicantManager *self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NM_SUPPL_CAP_MASK_NONE);
+
+	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->capabilities;
 }
 
 /*****************************************************************************/
 
 static void
-_sup_iface_last_ref (gpointer data,
-                     GObject *object,
-                     gboolean is_last_ref)
+_caps_set (NMSupplicantManagerPrivate *priv,
+           NMSupplCapType type,
+           NMTernary value)
 {
-	NMSupplicantManager *self = data;
-	NMSupplicantManagerPrivate *priv;
-	NMSupplicantInterface *sup_iface = (NMSupplicantInterface *) object;
-	const char *op;
+	priv->capabilities = NM_SUPPL_CAP_MASK_SET (priv->capabilities, type, value);
+}
 
-	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
-	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (sup_iface));
-	g_return_if_fail (is_last_ref);
+static char
+_caps_to_char (NMSupplicantManagerPrivate *priv,
+               NMSupplCapType type)
+{
+	NMTernary val;
+
+	val = NM_SUPPL_CAP_MASK_GET (priv->capabilities, type);
+	if (val == NM_TERNARY_TRUE)
+		return '+';
+	if (val == NM_TERNARY_FALSE)
+		return '-';
+	return '?';
+}
 
-	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+/*****************************************************************************/
 
-	if (!g_slist_find (priv->ifaces, sup_iface))
-		g_return_if_reached ();
-
-	/* Ask wpa_supplicant to remove this interface */
-	if (   priv->running
-	    && priv->proxy
-	    && (op = nm_supplicant_interface_get_object_path (sup_iface))) {
-		g_dbus_proxy_call (priv->proxy,
-		                   "RemoveInterface",
-		                   g_variant_new ("(o)", op),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   3000,
-		                   NULL,
-		                   NULL,
-		                   NULL);
-	}
+static void
+_dbus_call_remove_interface (GDBusConnection *dbus_connection,
+                             const char *name_owner,
+                             const char *iface_path)
+{
+	nm_assert (G_IS_DBUS_CONNECTION (dbus_connection));
+	nm_assert (name_owner);
+	nm_assert (iface_path);
+
+	g_dbus_connection_call (dbus_connection,
+	                        name_owner,
+	                        NM_WPAS_DBUS_PATH,
+	                        NM_WPAS_DBUS_INTERFACE,
+	                        "RemoveInterface",
+	                        g_variant_new ("(o)", iface_path),
+	                        G_VARIANT_TYPE ("()"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        10000,
+	                        NULL,
+	                        NULL,
+	                        NULL);
+}
 
-	priv->ifaces = g_slist_remove (priv->ifaces, sup_iface);
-	g_object_remove_toggle_ref ((GObject *) sup_iface, _sup_iface_last_ref, self);
+void
+_nm_supplicant_manager_dbus_call_remove_interface (NMSupplicantManager *self,
+                                                   const char *name_owner,
+                                                   const char *iface_path)
+{
+	_dbus_call_remove_interface (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_connection,
+	                             name_owner,
+	                             iface_path);
 }
 
+/*****************************************************************************/
+
 static void
 on_supplicant_wfd_ies_set (GObject *source_object,
-                           GAsyncResult *res,
+                           GAsyncResult *result,
                            gpointer user_data)
 {
-	gs_unref_variant GVariant *result = NULL;
+	gs_unref_variant GVariant *res = NULL;
 	gs_free_error GError *error = NULL;
 
-	result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
-
-	if (!result)
-		_LOGW ("failed to set WFD IEs on wpa_supplicant: %s", error->message);
+	res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), result, &error);
+	if (!res)
+		_LOGD ("failed to set WFD IEs on wpa_supplicant: %s", error->message);
 }
 
 /**
@@ -140,172 +245,680 @@ nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self,
 {
 	NMSupplicantManagerPrivate *priv;
 	GVariantBuilder params;
-	GVariant *val;
 
 	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
 
 	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	_LOGD ("setting WFD IEs for P2P operation");
+	if (!priv->name_owner)
+		return;
 
-	if (wfd_ies)
-		val = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                 g_bytes_get_data (wfd_ies, NULL),
-		                                 g_bytes_get_size (wfd_ies),
-		                                 sizeof (guint8));
-	else
-		val = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                 NULL, 0, sizeof (guint8));
+	_LOGD ("setting WFD IEs for P2P operation on %s", priv->name_owner->str);
 
 	g_variant_builder_init (&params, G_VARIANT_TYPE ("(ssv)"));
 
-	g_variant_builder_add (&params, "s", g_dbus_proxy_get_interface_name (priv->proxy));
+	g_variant_builder_add (&params, "s", NM_WPAS_DBUS_INTERFACE);
 	g_variant_builder_add (&params, "s", "WFDIEs");
-	g_variant_builder_add_value (&params, g_variant_new_variant (val));
+	g_variant_builder_add_value (&params,
+	                             g_variant_new_variant (nm_utils_gbytes_to_variant_ay (wfd_ies)));
 
-	g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
-	                        g_dbus_proxy_get_name (priv->proxy),
-	                        g_dbus_proxy_get_object_path (priv->proxy),
-	                        "org.freedesktop.DBus.Properties",
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->name_owner->str,
+	                        NM_WPAS_DBUS_PATH,
+	                        DBUS_INTERFACE_PROPERTIES,
 	                        "Set",
 	                        g_variant_builder_end (&params),
-	                        G_VARIANT_TYPE_UNIT,
+	                        G_VARIANT_TYPE ("()"),
 	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
-	                        1000,
+	                        3000,
 	                        NULL,
 	                        on_supplicant_wfd_ies_set,
 	                        NULL);
 }
 
-/**
- * nm_supplicant_manager_create_interface:
- * @self: the #NMSupplicantManager
- * @ifname: the interface for which to obtain the supplicant interface
- * @is_wireless: whether the interface is supposed to be wireless.
- *
- * Note: the manager owns a reference to the instance and the only way to
- *   get the manager to release it, is by dropping all other references
- *   to the supplicant-interface (or destroying the manager).
- *
- * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL.
- *   Must be unrefed at the end.
- * */
-NMSupplicantInterface *
+/*****************************************************************************/
+
+static gboolean
+_poke_name_owner_timeout_cb (gpointer user_data)
+{
+	NMSupplicantManager *self = user_data;
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
+	gboolean available_changed = FALSE;
+
+	nm_assert (!priv->name_owner);
+
+	priv->poke_name_owner_timeout_id = 0;
+	nm_clear_g_cancellable (&priv->poke_name_owner_cancellable);
+
+	_LOGT ("poke service \"%s\" failed for good with timeout%s",
+	       NM_WPAS_DBUS_SERVICE,
+	         (priv->available == NM_TERNARY_DEFAULT)
+	       ? " (set as not available)"
+	       : "");
+
+	if (priv->available == NM_TERNARY_DEFAULT) {
+		/* the available flag usually only changes together with the name-owner.
+		 * However, if we tries to poke the service but failed to start it (with
+		 * timeout), was also set it as (hard) not available. */
+		priv->available = NM_TERNARY_FALSE;
+		nm_clear_g_source (&priv->available_reset_id);
+		priv->available_reset_id = g_timeout_add_seconds (60,
+		                                                  _available_reset_cb,
+		                                                  self);
+		available_changed = TRUE;
+	}
+
+	nm_utils_error_set (&error,
+	                    NM_UTILS_ERROR_UNKNOWN,
+	                    "Failed to D-Bus activate wpa_supplicant service");
+
+	_create_iface_proceed_all (self, error);
+
+	if (available_changed) {
+		/* We delay the emitting of the notification after aborting all
+		 * create-iface handles. */
+		g_signal_emit (self, signals[AVAILABLE_CHANGED], 0);
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_poke_name_owner_cb (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+	gs_unref_variant GVariant *res = NULL;
+	gs_free_error GError *error = NULL;
+
+	res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+	if (nm_utils_error_is_cancelled (error))
+		return;
+
+	if (!res)
+		_LOGT ("poke service \"%s\" failed: %s", NM_WPAS_DBUS_SERVICE, error->message);
+	else
+		_LOGT ("poke service \"%s\" succeeded", NM_WPAS_DBUS_SERVICE);
+
+	/* in both cases, we react the same: we wait for the name owner to appear
+	 * or hit the timeout. */
+}
+
+static void
+_poke_name_owner (NMSupplicantManager *self)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	if (priv->poke_name_owner_cancellable)
+		return;
+
+	_LOGT ("poke service \"%s\"...", NM_WPAS_DBUS_SERVICE);
+
+	priv->poke_name_owner_cancellable = g_cancellable_new ();
+	priv->poke_name_owner_timeout_id = g_timeout_add (3000,
+	                                                  _poke_name_owner_timeout_cb,
+	                                                  self);
+	nm_dbus_connection_call_start_service_by_name (priv->dbus_connection,
+	                                               NM_WPAS_DBUS_SERVICE,
+	                                               5000,
+	                                               priv->poke_name_owner_cancellable,
+	                                               _poke_name_owner_cb,
+	                                               self);
+}
+
+/*****************************************************************************/
+
+static void
+_create_iface_complete (NMSupplMgrCreateIfaceHandle *handle,
+                        NMSupplicantInterface *supp_iface,
+                        GError *error)
+{
+	nm_assert (!supp_iface || NM_IS_SUPPLICANT_INTERFACE (supp_iface));
+	nm_assert ((!!supp_iface) != (!!error));
+
+	c_list_unlink (&handle->create_iface_lst);
+
+	nm_clear_g_source (&handle->fail_on_idle_id);
+
+	if (handle->callback) {
+		NMSupplicantManagerCreateInterfaceCb callback;
+
+		nm_assert (NM_IS_SUPPLICANT_MANAGER (handle->self));
+
+		callback = handle->callback;
+		handle->callback = NULL;
+		callback (handle->self,
+		          handle,
+		          supp_iface,
+		          error,
+		          handle->callback_user_data);
+	}
+
+	g_clear_error (&handle->fail_on_idle_error);
+
+	g_clear_object (&handle->self);
+
+	if (handle->shutdown_handle) {
+		/* we have a pending CreateInterface request. We keep the handle
+		 * instance alive. This is to remove the device again, once the
+		 * request completes. */
+		return;
+	}
+
+	nm_clear_g_cancellable (&handle->cancellable);
+	nm_ref_string_unref (handle->name_owner);
+
+	nm_g_slice_free_fcn (handle);
+}
+
+static void
+_create_iface_add (NMSupplicantManager *self,
+                   NMSupplMgrCreateIfaceHandle *handle,
+                   const char *iface_path_str,
+                   gboolean created_by_us)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	nm_auto_ref_string NMRefString *iface_path = NULL;
+	gs_unref_object NMSupplicantInterface *supp_iface = NULL;
+
+	iface_path = nm_ref_string_new (iface_path_str);
+
+	supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path);
+	if (supp_iface) {
+		/* Now this is odd... Reuse the same interface. */
+		g_object_ref (supp_iface);
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: interface %s on %s created (already existing)",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       iface_path_str,
+		       priv->name_owner->str);
+		_create_iface_complete (handle, supp_iface, NULL);
+		return;
+	}
+
+	_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: interface %s on %s created%s",
+	       NM_HASH_OBFUSCATE_PTR (handle),
+	       iface_path_str,
+	       priv->name_owner->str,
+	       created_by_us ? " (created by us)" : "");
+
+	supp_iface = nm_supplicant_interface_new (self,
+	                                          iface_path,
+	                                          handle->ifindex,
+	                                          handle->driver);
+
+	_supp_iface_add (self, iface_path, supp_iface);
+
+	_create_iface_complete (handle, supp_iface, NULL);
+}
+
+static void
+_create_iface_dbus_call_get_interface_cb (GObject *source,
+                                          GAsyncResult *result,
+                                          gpointer user_data)
+{
+	GDBusConnection *dbus_connection = G_DBUS_CONNECTION (source);
+	NMSupplMgrCreateIfaceHandle *handle;
+	NMSupplicantManager *self;
+	NMSupplicantManagerPrivate *priv;
+	gs_unref_variant GVariant *res = NULL;
+	gs_free_error GError *error = NULL;
+	const char *iface_path_str;
+
+	res = g_dbus_connection_call_finish (dbus_connection, result, &error);
+
+	if (nm_utils_error_is_cancelled (error))
+		return;
+
+	handle = user_data;
+	nm_assert (handle->callback);
+
+	self = handle->self;
+	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	nm_assert (handle->name_owner == priv->name_owner);
+
+	if (!res) {
+		char ifname[NMP_IFNAMSIZ];
+
+		if (   handle->create_iface_try_count < CREATE_IFACE_TRY_COUNT_MAX
+		    && _nm_dbus_error_has_name (error, NM_WPAS_ERROR_UNKNOWN_IFACE)
+		    && nm_platform_if_indextoname (NM_PLATFORM_GET, handle->ifindex, ifname)) {
+			/* Before, supplicant told us the interface existed. Was there a race?
+			 * Try again. */
+			_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call failed to get interface. Try to create it again (ifname \"%s\")",
+			       NM_HASH_OBFUSCATE_PTR (handle),
+			       ifname);
+			_create_iface_dbus_call_create_interface (self, handle, ifname);
+			return;
+		}
+
+		g_clear_object (&handle->cancellable);
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call to get interface failed: %s",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       error->message);
+		_create_iface_complete (handle, NULL, error);
+		return;
+	}
+
+	g_clear_object (&handle->cancellable);
+
+	g_variant_get (res, "(&o)", &iface_path_str);
+
+	_create_iface_add (self, handle, iface_path_str, FALSE);
+}
+
+static void
+_create_iface_dbus_call_create_interface_cb (GObject *source,
+                                             GAsyncResult *result,
+                                             gpointer user_data)
+{
+	GDBusConnection *dbus_connection = G_DBUS_CONNECTION (source);
+	NMSupplMgrCreateIfaceHandle *handle = user_data;
+	NMSupplicantManager *self;
+	NMSupplicantManagerPrivate *priv;
+	gs_unref_variant GVariant *res = NULL;
+	gs_free_error GError *error = NULL;
+	const char *iface_path_str;
+	char ifname[NMP_IFNAMSIZ];
+
+	res = g_dbus_connection_call_finish (dbus_connection, result, &error);
+
+	nm_shutdown_wait_obj_unregister (g_steal_pointer (&handle->shutdown_handle));
+
+	if (!res) {
+		if (   handle->callback
+		    && ({ nm_assert (handle->self); TRUE; })
+		    && _nm_dbus_error_has_name (error, NM_WPAS_ERROR_EXISTS_ERROR)
+		    && nm_platform_if_indextoname (NM_PLATFORM_GET, handle->ifindex, ifname)) {
+			self = handle->self;
+			_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call failed to create interface. Try to get existing interface (ifname \"%s\")",
+			       NM_HASH_OBFUSCATE_PTR (handle),
+			       ifname);
+			_create_iface_dbus_call_get_interface (self, handle, ifname);
+			return;
+		}
+		g_clear_object (&handle->cancellable);
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call failed: %s",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       error->message);
+		_create_iface_complete (handle, NULL, error);
+		return;
+	}
+
+	g_clear_object (&handle->cancellable);
+
+	self = handle->self;
+	priv =   self
+	       ? NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)
+	       : NULL;
+
+	g_variant_get (res, "(&o)", &iface_path_str);
+
+	if (   !handle->callback
+	    || priv->name_owner != handle->name_owner) {
+		if (!handle->callback) {
+			_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: request already cancelled but still remove interface %s in %s",
+			       NM_HASH_OBFUSCATE_PTR (handle),
+			       iface_path_str,
+			       handle->name_owner->str);
+			nm_utils_error_set (&error,
+			                    NM_UTILS_ERROR_UNKNOWN,
+			                    "Request already cancelled");
+		} else {
+			_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: name owner changed, still remove interface %s in %s",
+			       NM_HASH_OBFUSCATE_PTR (handle),
+			       iface_path_str,
+			       handle->name_owner->str);
+			nm_utils_error_set (&error,
+			                    NM_UTILS_ERROR_UNKNOWN,
+			                    "The name owner changed since creating the interface");
+		}
+		_dbus_call_remove_interface (dbus_connection,
+		                             handle->name_owner->str,
+		                             iface_path_str);
+		_create_iface_complete (handle, NULL, error);
+		return;
+	}
+
+	_create_iface_add (self, handle, iface_path_str, TRUE);
+}
+
+static void
+_create_iface_dbus_call_get_interface (NMSupplicantManager *self,
+                                       NMSupplMgrCreateIfaceHandle *handle,
+                                       const char *ifname)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	nm_assert (handle->cancellable);
+	nm_assert (!handle->shutdown_handle);
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->name_owner->str,
+	                        NM_WPAS_DBUS_PATH,
+	                        NM_WPAS_DBUS_INTERFACE,
+	                        "GetInterface",
+	                        g_variant_new ("(s)", ifname),
+	                        G_VARIANT_TYPE ("(o)"),
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        5000,
+	                        handle->cancellable,
+	                        _create_iface_dbus_call_get_interface_cb,
+	                        handle);
+}
+
+static void
+_create_iface_dbus_call_create_interface (NMSupplicantManager *self,
+                                          NMSupplMgrCreateIfaceHandle *handle,
+                                          const char *ifname)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	GVariantBuilder builder;
+
+	nm_assert (priv->name_owner == handle->name_owner);
+	nm_assert (handle->cancellable);
+	nm_assert (!handle->shutdown_handle);
+	nm_assert (handle->create_iface_try_count <= CREATE_IFACE_TRY_COUNT_MAX);
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+	g_variant_builder_add (&builder,
+	                       "{sv}",
+	                       "Driver",
+	                       g_variant_new_string (nm_supplicant_driver_to_string (handle->driver)));
+	g_variant_builder_add (&builder,
+	                       "{sv}",
+	                       "Ifname",
+	                       g_variant_new_string (ifname));
+
+	handle->shutdown_handle = nm_shutdown_wait_obj_register_cancellable_full (handle->cancellable,
+	                                                                          g_strdup_printf ("wpas-create-" NM_HASH_OBFUSCATE_PTR_FMT,
+	                                                                                           NM_HASH_OBFUSCATE_PTR (handle)),
+	                                                                          TRUE);
+	handle->create_iface_try_count++;
+	g_dbus_connection_call (priv->dbus_connection,
+	                        handle->name_owner->str,
+	                        NM_WPAS_DBUS_PATH,
+	                        NM_WPAS_DBUS_INTERFACE,
+	                        "CreateInterface",
+	                        g_variant_new ("(a{sv})", &builder),
+	                        G_VARIANT_TYPE ("(o)"),
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        5000,
+	                        handle->cancellable,
+	                        _create_iface_dbus_call_create_interface_cb,
+	                        handle);
+}
+
+static void
+_create_iface_dbus_start (NMSupplicantManager *self,
+                          NMSupplMgrCreateIfaceHandle *handle)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	char ifname[NMP_IFNAMSIZ];
+
+	nm_assert (priv->name_owner);
+	nm_assert (!handle->cancellable);
+
+	if (!nm_platform_if_indextoname (NM_PLATFORM_GET, handle->ifindex, ifname)) {
+		nm_utils_error_set (&handle->fail_on_idle_error,
+		                    NM_UTILS_ERROR_UNKNOWN,
+		                    "Cannot find interface %d",
+		                    handle->ifindex);
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: creating interface fails to find interface name for ifindex %d",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       handle->ifindex);
+		handle->fail_on_idle_id = g_idle_add (_create_iface_fail_on_idle_cb, handle);
+		return;
+	}
+
+	/* Our handle keeps @self alive. That means, when NetworkManager shall shut
+	 * down, it's the responsibility of the callers to cancel the handles,
+	 * to initiate coordinated shutdown.
+	 *
+	 * However, we now issue a CreateInterface call. Even if the handle gets cancelled
+	 * (because of shutdown, or because the caller is no longer interested in the
+	 * result), we don't want to cancel this request. Instead, we want to get
+	 * the interface path and remove it right away.
+	 *
+	 * That means, the D-Bus call cannot be cancelled (because we always care about
+	 * the result). Only the @handle can be cancelled, but parts of the handle will
+	 * stick around to complete the task.
+	 *
+	 * See also handle->shutdown_handle.
+	 */
+	handle->name_owner = nm_ref_string_ref (priv->name_owner);
+	handle->cancellable = g_cancellable_new ();
+	_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: creating interface (ifname \"%s\")...",
+	       NM_HASH_OBFUSCATE_PTR (handle),
+	       ifname);
+	_create_iface_dbus_call_create_interface (self, handle, ifname);
+}
+
+static gboolean
+_create_iface_fail_on_idle_cb (gpointer user_data)
+{
+	NMSupplMgrCreateIfaceHandle *handle = user_data;
+
+	handle->fail_on_idle_id = 0;
+
+	_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: fail with internal error: %s",
+	       NM_HASH_OBFUSCATE_PTR (handle),
+	       handle->fail_on_idle_error->message);
+
+	_create_iface_complete (handle, NULL, handle->fail_on_idle_error);
+	return G_SOURCE_REMOVE;
+}
+
+NMSupplMgrCreateIfaceHandle *
 nm_supplicant_manager_create_interface (NMSupplicantManager *self,
-                                        const char *ifname,
-                                        NMSupplicantDriver driver)
+                                        int ifindex,
+                                        NMSupplicantDriver driver,
+                                        NMSupplicantManagerCreateInterfaceCb callback,
+                                        gpointer user_data)
 {
 	NMSupplicantManagerPrivate *priv;
-	NMSupplicantInterface *iface;
-	GSList *ifaces;
+	NMSupplMgrCreateIfaceHandle *handle;
 
 	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
-	g_return_val_if_fail (ifname != NULL, NULL);
+	g_return_val_if_fail (ifindex > 0, NULL);
+	g_return_val_if_fail (callback, NULL);
+	nm_assert (nm_supplicant_driver_to_string (driver));
 
 	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	_LOGD ("(%s): creating new supplicant interface", ifname);
+	handle = g_slice_new (NMSupplMgrCreateIfaceHandle);
+	*handle = (NMSupplMgrCreateIfaceHandle) {
+		.self               = g_object_ref (self),
+		.callback           = callback,
+		.callback_user_data = user_data,
+		.driver             = driver,
+		.ifindex            = ifindex,
+	};
+	c_list_link_tail (&priv->create_iface_lst_head, &handle->create_iface_lst);
+
+	if (!priv->dbus_connection) {
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). Fail bacause no D-Bus connection to talk to wpa_supplicant...",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       ifindex,
+		       nm_supplicant_driver_to_string (driver));
+		nm_utils_error_set (&handle->fail_on_idle_error,
+		                    NM_UTILS_ERROR_UNKNOWN,
+		                    "No D-Bus connection to talk to wpa_supplicant");
+		handle->fail_on_idle_id = g_idle_add (_create_iface_fail_on_idle_cb, handle);
+		return handle;
+	}
 
-	/* assert against not requesting duplicate interfaces. */
-	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) {
-		if (g_strcmp0 (nm_supplicant_interface_get_ifname (ifaces->data), ifname) == 0)
-			g_return_val_if_reached (NULL);
+	if (!priv->name_owner) {
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). %s",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       ifindex,
+		       nm_supplicant_driver_to_string (driver),
+		         priv->poke_name_owner_cancellable
+		       ? "Waiting for supplicant..."
+		       : "Poke supplicant...");
+		_poke_name_owner (self);
+		return handle;
 	}
 
-	iface = nm_supplicant_interface_new (ifname,
-	                                     NULL,
-	                                     driver,
-	                                     priv->fast_support,
-	                                     priv->ap_support,
-	                                     priv->pmf_support,
-	                                     priv->fils_support,
-	                                     priv->p2p_support,
-	                                     priv->mesh_support,
-	                                     priv->wfd_support,
-	                                     priv->ft_support,
-	                                     priv->sha384_support);
-
-	priv->ifaces = g_slist_prepend (priv->ifaces, iface);
-	g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self);
-
-	/* If we're making the supplicant take a time out for a bit, don't
-	 * let the supplicant interface start immediately, just let it hang
-	 * around in INIT state until we're ready to talk to the supplicant
-	 * again.
-	 */
-	if (is_available (self))
-		nm_supplicant_interface_set_supplicant_available (iface, TRUE);
+	if (priv->get_capabilities_cancellable) {
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). Waiting to fetch capabilities for %s...",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       ifindex,
+		       nm_supplicant_driver_to_string (driver),
+		       priv->name_owner->str);
+		return handle;
+	}
 
-	return iface;
+	_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). create interface on %s...",
+	       NM_HASH_OBFUSCATE_PTR (handle),
+	       ifindex,
+	       nm_supplicant_driver_to_string (driver),
+	       priv->name_owner->str);
+
+	_create_iface_dbus_start (self, handle);
+	return handle;
+}
+
+static void
+_create_iface_proceed_all (NMSupplicantManager *self,
+                           GError *error)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	NMSupplMgrCreateIfaceHandle *handle;
+
+	nm_assert (error || priv->name_owner);
+	nm_assert (error || !priv->get_capabilities_cancellable);
+
+	if (c_list_is_empty (&priv->create_iface_lst_head))
+		return;
+
+	if (error) {
+		CList alt_list;
+
+		/* we move the handles we want to proceed to a alternative list.
+		 * That is, because we invoke callbacks to the caller, who might
+		 * create another request right away. We don't want to proceed
+		 * that one. */
+		c_list_init (&alt_list);
+		c_list_splice (&alt_list, &priv->create_iface_lst_head);
+
+		while ((handle = c_list_last_entry (&alt_list, NMSupplMgrCreateIfaceHandle, create_iface_lst))) {
+			/* We don't need to keep @self alive. Every handle holds a reference already. */
+			_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: create interface failed: %s",
+			       NM_HASH_OBFUSCATE_PTR (handle),
+			       error->message);
+			_create_iface_complete (handle, NULL, error);
+		}
+		return;
+	}
+
+	/* start all the handles. This does not invoke callbacks, so the list of handles
+	 * cannot be modified while we iterate it. */
+	c_list_for_each_entry (handle, &priv->create_iface_lst_head, create_iface_lst) {
+		_LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: create interface on %s...",
+		       NM_HASH_OBFUSCATE_PTR (handle),
+		       priv->name_owner->str);
+		_create_iface_dbus_start (self, handle);
+	}
+}
+
+void
+nm_supplicant_manager_create_interface_cancel (NMSupplMgrCreateIfaceHandle *handle)
+{
+	gs_free_error GError *error = NULL;
+
+	if (!handle)
+		return;
+
+	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (handle->self));
+	g_return_if_fail (handle->callback);
+	nm_assert (!c_list_is_empty (&handle->create_iface_lst));
+
+	nm_utils_error_set_cancelled (&error, FALSE, NULL);
+	_create_iface_complete (handle, NULL, error);
 }
 
-/**
- * nm_supplicant_manager_create_interface_from_path:
- * @self: the #NMSupplicantManager
- * @object_path: the DBus object path for which to obtain the supplicant interface
- *
- * Note: the manager owns a reference to the instance and the only way to
- *   get the manager to release it, is by dropping all other references
- *   to the supplicant-interface (or destroying the manager).
- *
- * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL.
- *   Must be unrefed at the end.
- * */
 NMSupplicantInterface *
 nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self,
                                                   const char *object_path)
 {
 	NMSupplicantManagerPrivate *priv;
-	NMSupplicantInterface *iface;
-	GSList *ifaces;
+	NMSupplicantInterface *supp_iface;
+	nm_auto_ref_string NMRefString *iface_path = NULL;
 
 	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
-	g_return_val_if_fail (object_path != NULL, NULL);
+	g_return_val_if_fail (object_path, NULL);
 
 	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	_LOGD ("creating new supplicant interface for dbus path %s", object_path);
+	iface_path = nm_ref_string_new (object_path);
 
-	/* assert against not requesting duplicate interfaces. */
-	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) {
-		if (g_strcmp0 (nm_supplicant_interface_get_object_path (ifaces->data), object_path) == 0)
-			g_return_val_if_reached (NULL);
-	}
+	supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path);
 
-	iface = nm_supplicant_interface_new (NULL,
-	                                     object_path,
-	                                     NM_SUPPLICANT_DRIVER_WIRELESS,
-	                                     priv->fast_support,
-	                                     priv->ap_support,
-	                                     priv->pmf_support,
-	                                     priv->fils_support,
-	                                     priv->p2p_support,
-	                                     priv->mesh_support,
-	                                     priv->wfd_support,
-	                                     priv->ft_support,
-	                                     priv->sha384_support);
-
-	priv->ifaces = g_slist_prepend (priv->ifaces, iface);
-	g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self);
-
-	/* If we're making the supplicant take a time out for a bit, don't
-	 * let the supplicant interface start immediately, just let it hang
-	 * around in INIT state until we're ready to talk to the supplicant
-	 * again.
-	 */
-	if (is_available (self))
-		nm_supplicant_interface_set_supplicant_available (iface, TRUE);
+	if (supp_iface)
+		return g_object_ref (supp_iface);
+
+	supp_iface = nm_supplicant_interface_new (self,
+	                                          iface_path,
+	                                          0,
+	                                          NM_SUPPLICANT_DRIVER_UNKNOWN);
+
+	_supp_iface_add (self, iface_path, supp_iface);
 
-	return iface;
+	return supp_iface;
 }
 
+/*****************************************************************************/
+
 static void
-update_capabilities (NMSupplicantManager *self)
+_dbus_interface_removed_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)
 {
+	NMSupplicantManager *self = user_data;
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	GSList *ifaces;
-	const char **array;
-	GVariant *value;
+	NMSupplicantInterface *supp_iface;
+	const char *iface_path_str;
+	nm_auto_ref_string NMRefString *iface_path = NULL;
+
+	nm_assert (nm_streq (sender_name, priv->name_owner->str));
+
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+		return;
+
+	g_variant_get (parameters, "(&o)", &iface_path_str);
+
+	iface_path = nm_ref_string_new (iface_path_str);
+
+	supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path);
+	if (!supp_iface)
+		return;
+
+	_supp_iface_remove_one (self, supp_iface, FALSE, "InterfaceRemoved signal from wpa_supplicant");
+}
+
+/*****************************************************************************/
+
+static void
+_dbus_get_capabilities_cb (GVariant *res,
+                           GError *error,
+                           gpointer user_data)
+{
+	NMSupplicantManager *self;
+	NMSupplicantManagerPrivate *priv;
+
+	if (nm_utils_error_is_cancelled (error))
+		return;
+
+	self = user_data;
+	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->get_capabilities_cancellable);
 
 	/* The supplicant only advertises global capabilities if the following
 	 * commit has been applied:
@@ -316,269 +929,398 @@ update_capabilities (NMSupplicantManager *self)
 	 *
 	 * dbus: Add global capabilities property
 	 */
-	priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-	priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-	priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_AP,     NM_TERNARY_DEFAULT);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_PMF,    NM_TERNARY_DEFAULT);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_FILS,   NM_TERNARY_DEFAULT);
+
 	/* Support for the following is newer than the capabilities property */
-	priv->p2p_support = NM_SUPPLICANT_FEATURE_NO;
-	priv->ft_support = NM_SUPPLICANT_FEATURE_NO;
-	priv->sha384_support = NM_SUPPLICANT_FEATURE_NO;
-	priv->mesh_support = NM_SUPPLICANT_FEATURE_NO;
-
-	value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities");
-	if (value) {
-		if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) {
-			array = g_variant_get_strv (value, NULL);
-			priv->ap_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->pmf_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->fils_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->p2p_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->ft_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->sha384_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->mesh_support = NM_SUPPLICANT_FEATURE_NO;
-			if (array) {
-				if (g_strv_contains (array, "ap"))
-					priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "pmf"))
-					priv->pmf_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "fils"))
-					priv->fils_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "p2p"))
-					priv->p2p_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "ft"))
-					priv->ft_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "sha384"))
-					priv->sha384_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "mesh"))
-					priv->mesh_support = NM_SUPPLICANT_FEATURE_YES;
-				g_free (array);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_P2P,    NM_TERNARY_FALSE);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_FT,     NM_TERNARY_FALSE);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_SHA384, NM_TERNARY_FALSE);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_MESH,   NM_TERNARY_FALSE);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_FAST,   NM_TERNARY_FALSE);
+	_caps_set (priv, NM_SUPPL_CAP_TYPE_WFD,    NM_TERNARY_FALSE);
+
+	if (res) {
+		nm_auto_free_variant_iter GVariantIter *res_iter = NULL;
+		const char *res_key;
+		GVariant *res_val;
+
+		g_variant_get (res, "(a{sv})", &res_iter);
+		while (g_variant_iter_loop (res_iter, "{&sv}", &res_key, &res_val)) {
+			if (nm_streq (res_key, "Capabilities")) {
+				if (g_variant_is_of_type (res_val, G_VARIANT_TYPE_STRING_ARRAY)) {
+					gs_free const char **array = NULL;
+					const char **a;
+
+					array = g_variant_get_strv (res_val, NULL);
+					_caps_set (priv, NM_SUPPL_CAP_TYPE_AP,   NM_TERNARY_FALSE);
+					_caps_set (priv, NM_SUPPL_CAP_TYPE_PMF,  NM_TERNARY_FALSE);
+					_caps_set (priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_FALSE);
+					if (array) {
+						for (a = array; *a; a++) {
+							if (nm_streq (*a, "ap"))     { _caps_set (priv, NM_SUPPL_CAP_TYPE_AP,     NM_TERNARY_TRUE); continue; }
+							if (nm_streq (*a, "pmf"))    { _caps_set (priv, NM_SUPPL_CAP_TYPE_PMF,    NM_TERNARY_TRUE); continue; }
+							if (nm_streq (*a, "fils"))   { _caps_set (priv, NM_SUPPL_CAP_TYPE_FILS,   NM_TERNARY_TRUE); continue; }
+							if (nm_streq (*a, "p2p"))    { _caps_set (priv, NM_SUPPL_CAP_TYPE_P2P,    NM_TERNARY_TRUE); continue; }
+							if (nm_streq (*a, "ft"))     { _caps_set (priv, NM_SUPPL_CAP_TYPE_FT,     NM_TERNARY_TRUE); continue; }
+							if (nm_streq (*a, "sha384")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_SHA384, NM_TERNARY_TRUE); continue; }
+							if (nm_streq (*a, "mesh"))   { _caps_set (priv, NM_SUPPL_CAP_TYPE_MESH,   NM_TERNARY_TRUE); continue; }
+						}
+					}
+				}
+				continue;
 			}
-		}
-		g_variant_unref (value);
-	}
-
-	/* Tell all interfaces about results of the AP/PMF/FILS/P2P/FT/SHA384 check */
-	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) {
-		nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support);
-		nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support);
-		nm_supplicant_interface_set_fils_support (ifaces->data, priv->fils_support);
-		nm_supplicant_interface_set_p2p_support (ifaces->data, priv->p2p_support);
-		nm_supplicant_interface_set_ft_support (ifaces->data, priv->ft_support);
-		nm_supplicant_interface_set_sha384_support (ifaces->data, priv->sha384_support);
-		nm_supplicant_interface_set_mesh_support (ifaces->data, priv->mesh_support);
-	}
-
-	_LOGD ("AP mode is %ssupported",
-	       (priv->ap_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->ap_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("PMF is %ssupported",
-	       (priv->pmf_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->pmf_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("FILS is %ssupported",
-	       (priv->fils_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->fils_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("P2P is %ssupported",
-	       (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->p2p_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("FT is %ssupported",
-	       (priv->ft_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->ft_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("SHA384 is %ssupported",
-	       (priv->sha384_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->sha384_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("Mesh is %ssupported",
-	       (priv->mesh_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->mesh_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-
-	/* EAP-FAST */
-	priv->fast_support = NM_SUPPLICANT_FEATURE_NO;
-	value = g_dbus_proxy_get_cached_property (priv->proxy, "EapMethods");
-	if (value) {
-		if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) {
-			array = g_variant_get_strv (value, NULL);
-			if (array) {
-				const char **a;
-
-				for (a = array; *a; a++) {
-					if (g_ascii_strcasecmp (*a, "FAST") == 0) {
-						priv->fast_support = NM_SUPPLICANT_FEATURE_YES;
-						break;
+			if (nm_streq (res_key, "EapMethods")) {
+				if (g_variant_is_of_type (res_val, G_VARIANT_TYPE_STRING_ARRAY)) {
+					gs_free const char **array = NULL;
+					const char **a;
+
+					array = g_variant_get_strv (res_val, NULL);
+					if (array) {
+						for (a = array; *a; a++) {
+							if (g_ascii_strcasecmp (*a, "FAST") == 0) {
+								_caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_TRUE);
+								break;
+							}
+						}
 					}
 				}
-				g_free (array);
+				continue;
+			}
+			if (nm_streq (res_key, "WFDIEs")) {
+				_caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_TRUE);
+				continue;
 			}
 		}
-		g_variant_unref (value);
 	}
 
-	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next)
-		nm_supplicant_interface_set_fast_support (ifaces->data, priv->fast_support);
+	_LOGD ("supported features:"
+	       " AP%c"
+	       " PMF%c"
+	       " FILS%c"
+	       " P2P%c"
+	       " FT%c"
+	       " SHA384%c"
+	       " MESH%c"
+	       " FAST%c"
+	       " WFD%c"
+	       "",
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_AP),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_PMF),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_FILS),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_P2P),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_FT),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_SHA384),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_MESH),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_FAST),
+	       _caps_to_char (priv, NM_SUPPL_CAP_TYPE_WFD));
+
+	nm_assert (g_hash_table_size (priv->supp_ifaces) == 0);
+	nm_assert (c_list_is_empty (&priv->supp_lst_head));
+
+	_create_iface_proceed_all (self, NULL);
+}
 
-	_LOGD ("EAP-FAST is %ssupported",
-	       (priv->fast_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->fast_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
+/*****************************************************************************/
 
-	priv->wfd_support = NM_SUPPLICANT_FEATURE_NO;
-	value = g_dbus_proxy_get_cached_property (priv->proxy, "WFDIEs");
-	if (value) {
-		priv->wfd_support = NM_SUPPLICANT_FEATURE_YES;
-		g_variant_unref (value);
-	}
+void
+_nm_supplicant_manager_unregister_interface (NMSupplicantManager *self,
+                                             NMSupplicantInterface *supp_iface)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next)
-		nm_supplicant_interface_set_wfd_support (ifaces->data, priv->fast_support);
+	nm_assert (NM_IS_SUPPLICANT_INTERFACE (supp_iface));
+	nm_assert (c_list_contains (&NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->supp_lst_head, &supp_iface->supp_lst));
 
-	_LOGD ("WFD is %ssupported",
-	       (priv->wfd_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->wfd_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
+	c_list_unlink (&supp_iface->supp_lst);
+	if (!g_hash_table_remove (priv->supp_ifaces, nm_supplicant_interface_get_object_path (supp_iface)))
+		nm_assert_not_reached ();
 }
 
 static void
-availability_changed (NMSupplicantManager *self, gboolean available)
+_supp_iface_add (NMSupplicantManager *self,
+                 NMRefString *iface_path,
+                 NMSupplicantInterface *supp_iface)
 {
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	GSList *ifaces, *iter;
-
-	if (!priv->ifaces)
-		return;
 
-	/* setting the supplicant as unavailable might cause the caller to unref
-	 * the supplicant (and thus remove the instance from the list of interfaces.
-	 * Delay that by taking an additional reference first. */
-	ifaces = g_slist_copy (priv->ifaces);
-	for (iter = ifaces; iter; iter = iter->next)
-		g_object_ref (iter->data);
-	for (iter = ifaces; iter; iter = iter->next)
-		nm_supplicant_interface_set_supplicant_available (iter->data, available);
-	g_slist_free_full (ifaces, g_object_unref);
+	c_list_link_tail (&priv->supp_lst_head, &supp_iface->supp_lst);
+	if (!g_hash_table_insert (priv->supp_ifaces, iface_path, supp_iface))
+		nm_assert_not_reached ();
 }
 
 static void
-set_running (NMSupplicantManager *self, gboolean now_running)
+_supp_iface_remove_one (NMSupplicantManager *self,
+                        NMSupplicantInterface *supp_iface,
+                        gboolean force_remove_from_supplicant,
+                        const char *reason)
 {
-	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	gboolean old_available = is_available (self);
-	gboolean new_available;
+#if NM_MORE_ASSERTS
+	_nm_unused gs_unref_object NMSupplicantInterface *supp_iface_keep_alive = g_object_ref (supp_iface);
+#endif
+
+	nm_assert (NM_IS_SUPPLICANT_MANAGER (self));
+	nm_assert (NM_IS_SUPPLICANT_INTERFACE (supp_iface));
+	nm_assert (c_list_contains (&NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->supp_lst_head, &supp_iface->supp_lst));
+
+	_nm_supplicant_interface_set_state_down (supp_iface, force_remove_from_supplicant, reason);
 
-	priv->running = now_running;
-	new_available = is_available (self);
-	if (old_available != new_available)
-		availability_changed (self, new_available);
+	nm_assert (c_list_is_empty (&supp_iface->supp_lst));
 }
 
 static void
-set_die_count (NMSupplicantManager *self, guint new_die_count)
+_supp_iface_remove_all (NMSupplicantManager *self,
+                        gboolean force_remove_from_supplicant,
+                        const char *reason)
 {
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	gboolean old_available = is_available (self);
-	gboolean new_available;
+	NMSupplicantInterface *supp_iface;
 
-	priv->die_count = new_die_count;
-	new_available = is_available (self);
-	if (old_available != new_available)
-		availability_changed (self, new_available);
+	while ((supp_iface = c_list_first_entry (&priv->supp_lst_head, NMSupplicantInterface, supp_lst)))
+		_supp_iface_remove_one (self, supp_iface, force_remove_from_supplicant, reason);
 }
 
+/*****************************************************************************/
+
 static gboolean
-wpas_die_count_reset_cb (gpointer user_data)
+_available_reset_cb (gpointer user_data)
 {
-	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
+	NMSupplicantManager *self = user_data;
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	/* Reset the die count back to zero, which allows use of the supplicant again */
-	priv->die_count_reset_id = 0;
-	set_die_count (self, 0);
-	_LOGI ("wpa_supplicant die count reset");
-	return FALSE;
+	priv->available_reset_id = 0;
+	nm_assert (priv->available == NM_TERNARY_FALSE);
+	priv->available = NM_TERNARY_DEFAULT;
+	g_signal_emit (self, signals[AVAILABLE_CHANGED], 0);
+	return G_SOURCE_REMOVE;
 }
 
+/*****************************************************************************/
+
 static void
-name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
+name_owner_changed (NMSupplicantManager *self,
+                    const char *name_owner,
+                    gboolean first_time)
 {
-	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	char *owner;
-
-	g_return_if_fail (proxy == priv->proxy);
-
-	owner = g_dbus_proxy_get_name_owner (proxy);
-	_LOGI ("wpa_supplicant %s", owner ? "running" : "stopped");
-
-	if (owner) {
-		update_capabilities (self);
-		set_running (self, TRUE);
-	} else if (priv->running) {
-		/* Reschedule the die count reset timeout.  Every time the supplicant
-		 * dies we wait 10 seconds before resetting the counter.  If the
-		 * supplicant died more than twice before the timer is reset, then
-		 * we don't try to talk to the supplicant for a while.
-		 */
-		if (priv->die_count_reset_id)
-			g_source_remove (priv->die_count_reset_id);
-		priv->die_count_reset_id = g_timeout_add_seconds (10, wpas_die_count_reset_cb, self);
-		set_die_count (self, priv->die_count + 1);
-
-		if (die_count_exceeded (priv->die_count)) {
-			_LOGI ("wpa_supplicant die count %d; ignoring for 10 seconds",
-			       priv->die_count);
+	NMTernary available;
+	gboolean available_changed = FALSE;
+
+	nm_assert (!priv->get_name_owner_cancellable);
+	nm_assert (   !name_owner
+	           || name_owner[0]);
+	nm_assert (   (   first_time
+	               && !priv->name_owner)
+	           || (   !first_time
+	               && (!!priv->name_owner) != (!!name_owner)));
+
+	if (first_time) {
+		_LOGD ("wpa_supplicant name owner %s%s%s (%srunning)",
+		       NM_PRINT_FMT_QUOTE_STRING (name_owner),
+		       name_owner ? "" : "not ");
+	} else {
+		_LOGD ("wpa_supplicant name owner \"%s\" %s (%srunning)",
+		       name_owner ?: priv->name_owner->str,
+		       name_owner ? "disappeared" : "appeared",
+		       name_owner ? "" : "not ");
+	}
+
+	nm_ref_string_unref (priv->name_owner);
+	priv->name_owner = nm_ref_string_new (name_owner);
+
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->interface_removed_id);
+
+	if (name_owner) {
+		if (nm_clear_g_source (&priv->poke_name_owner_timeout_id))
+			_LOGT ("poke service \"%s\" completed with name owner change", NM_WPAS_DBUS_SERVICE);
+		nm_clear_g_cancellable (&priv->poke_name_owner_cancellable);
+	}
+
+	nm_clear_g_cancellable (&priv->get_capabilities_cancellable);
+
+	priv->capabilities = NM_SUPPL_CAP_MASK_NONE;
+	if (priv->name_owner) {
+		priv->get_capabilities_cancellable = g_cancellable_new ();
+		nm_dbus_connection_call_get_all (priv->dbus_connection,
+		                                 priv->name_owner->str,
+		                                 NM_WPAS_DBUS_PATH,
+		                                 NM_WPAS_DBUS_INTERFACE,
+		                                 5000,
+		                                 priv->get_capabilities_cancellable,
+		                                 _dbus_get_capabilities_cb,
+		                                 self);
+		priv->interface_removed_id = g_dbus_connection_signal_subscribe (priv->dbus_connection,
+		                                                                 priv->name_owner->str,
+		                                                                 NM_WPAS_DBUS_INTERFACE,
+		                                                                 "InterfaceRemoved",
+		                                                                 NULL,
+		                                                                 NULL,
+		                                                                 G_DBUS_SIGNAL_FLAGS_NONE,
+		                                                                 _dbus_interface_removed_cb,
+		                                                                 self,
+		                                                                 NULL);
+	}
+
+	/* if supplicant is running (has a name owner), we may use it.
+	 * If this is the first time, and supplicant is not running, we
+	 * may also use it (and assume that we probably could D-Bus activate
+	 * it).
+	 *
+	 * Otherwise, somebody else stopped supplicant. It's no longer useable to
+	 * us and we block auto starting it. The user has to start the service...
+	 *
+	 * Actually, below we reset the hard block after a short timeout. This
+	 * causes the caller to notify that supplicant may now by around and
+	 * retry to D-Bus activate it. */
+	if (priv->name_owner)
+		available = NM_TERNARY_TRUE;
+	else if (first_time)
+		available = NM_TERNARY_DEFAULT;
+	else
+		available = NM_TERNARY_FALSE;
+
+	if (priv->available != available) {
+		priv->available = available;
+		_LOGD ("supplicant is now %savailable",
+		         available == FALSE
+		       ? "not "
+		       : (  available == TRUE
+		          ? ""
+		          : "maybe "));
+		available_changed = TRUE;
+
+		nm_clear_g_source (&priv->available_reset_id);
+		if (available == NM_TERNARY_FALSE) {
+			/* reset the availability from a hard "no" to a "maybe" in a bit. */
+			priv->available_reset_id = g_timeout_add_seconds (60,
+			                                                  _available_reset_cb,
+			                                                  self);
 		}
+	}
+
+	_supp_iface_remove_all (self, TRUE, "name-owner changed");
 
-		priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-		priv->fast_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-		priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-		priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-		priv->ft_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-		priv->sha384_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
+	if (!priv->name_owner) {
+		if (priv->poke_name_owner_timeout_id) {
+			/* we are still poking for the service to start. Don't cancel
+			 * the pending create requests just yet. */
+		} else {
+			gs_free_error GError *local_error = NULL;
 
-		set_running (self, FALSE);
+			/* When we loose the name owner, we fail all pending creation requests. */
+			nm_utils_error_set (&local_error,
+			                    NM_UTILS_ERROR_UNKNOWN,
+			                    "Name owner lost");
+			_create_iface_proceed_all (self, local_error);
+		}
+	} else {
+		/* We got a name-owner, but we don't do anything. Instead let
+		 * _dbus_get_capabilities_cb() complete and kick of the create-iface
+		 * handles.
+		 *
+		 * Note that before the first name-owner change, all create-iface
+		 * requests fail right away. So we don't have to handle them here
+		 * (by starting to poke the service). */
 	}
 
-	g_free (owner);
+	if (available_changed)
+		g_signal_emit (self, signals[AVAILABLE_CHANGED], 0);
 }
 
 static void
-on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_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)
 {
-	NMSupplicantManager *self;
-	NMSupplicantManagerPrivate *priv;
-	GError *error = NULL;
-	GDBusProxy *proxy;
+	gs_unref_object NMSupplicantManager *self = g_object_ref (user_data);
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	const char *name_owner;
 
-	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
-	if (!proxy) {
-		_LOGW ("failed to acquire wpa_supplicant proxy: Wi-Fi and 802.1x will not be available (%s)",
-		       error->message);
-		g_clear_error (&error);
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
 		return;
+
+	if (priv->get_name_owner_cancellable)
+		return;
+
+	g_variant_get (parameters,
+	               "(&s&s&s)",
+	               NULL,
+	               NULL,
+	               &name_owner);
+
+	name_owner = nm_str_not_empty (name_owner);
+
+	if (nm_streq0 (name_owner, nm_ref_string_get_str (priv->name_owner)))
+		return;
+
+	if (   name_owner
+	    && priv->name_owner) {
+		/* odd, we directly switch from one name owner to the next. Can't allow that.
+		 * First clear the name owner before resetting. */
+		name_owner_changed (self, NULL, FALSE);
 	}
+	name_owner_changed (user_data, name_owner, FALSE);
+}
 
-	self = NM_SUPPLICANT_MANAGER (user_data);
+static void
+get_name_owner_cb (const char *name_owner,
+                   GError *error,
+                   gpointer user_data)
+{
+	NMSupplicantManager *self = user_data;
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	if (   !name_owner
+	    && nm_utils_error_is_cancelled (error))
+		return;
+
+	self = user_data;
 	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	priv->proxy = proxy;
-	g_signal_connect (priv->proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self);
-	name_owner_cb (priv->proxy, NULL, self);
+	g_clear_object (&priv->get_name_owner_cancellable);
+
+	name_owner_changed (self, nm_str_not_empty (name_owner), TRUE);
 }
 
 /*****************************************************************************/
 
-NM_DEFINE_SINGLETON_GETTER (NMSupplicantManager, nm_supplicant_manager_get, NM_TYPE_SUPPLICANT_MANAGER);
-
 static void
 nm_supplicant_manager_init (NMSupplicantManager *self)
 {
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	priv->cancellable = g_cancellable_new ();
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_NONE,
-	                          NULL,
-	                          WPAS_DBUS_SERVICE,
-	                          WPAS_DBUS_PATH,
-	                          WPAS_DBUS_INTERFACE,
-	                          priv->cancellable,
-	                          (GAsyncReadyCallback) on_proxy_acquired,
-	                          self);
+	nm_assert (priv->capabilities == NM_SUPPL_CAP_MASK_NONE);
+	nm_assert (priv->available == NM_TERNARY_FALSE);
+
+	priv->supp_ifaces = g_hash_table_new (nm_direct_hash, NULL);
+	c_list_init (&priv->supp_lst_head);
+	c_list_init (&priv->create_iface_lst_head);
+
+	priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
+
+	if (!priv->dbus_connection) {
+		_LOGI ("no D-Bus connection to talk to wpa_supplicant");
+		return;
+	}
+
+	priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+	                                                                                      NM_WPAS_DBUS_SERVICE,
+	                                                                                      name_owner_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+	priv->get_name_owner_cancellable = g_cancellable_new ();
+	nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+	                                        NM_WPAS_DBUS_SERVICE,
+	                                        -1,
+	                                        priv->get_name_owner_cancellable,
+	                                        get_name_owner_cb,
+	                                        self);
 }
 
 static void
@@ -586,22 +1328,32 @@ dispose (GObject *object)
 {
 	NMSupplicantManager *self = (NMSupplicantManager *) object;
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	GSList *ifaces;
 
-	nm_clear_g_source (&priv->die_count_reset_id);
+	_supp_iface_remove_all (self, TRUE, "NMSupplicantManager is disposing");
 
-	nm_clear_g_cancellable (&priv->cancellable);
+	nm_assert (c_list_is_empty (&priv->create_iface_lst_head));
 
-	if (priv->ifaces) {
-		for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next)
-			g_object_remove_toggle_ref (ifaces->data, _sup_iface_last_ref, self);
-		g_slist_free (priv->ifaces);
-		priv->ifaces = NULL;
-	}
+	nm_clear_g_source (&priv->available_reset_id);
+
+	priv->available = NM_TERNARY_FALSE;
+	nm_clear_pointer (&priv->name_owner, nm_ref_string_unref);
+
+	nm_clear_g_source (&priv->poke_name_owner_timeout_id);
+	nm_clear_g_cancellable (&priv->poke_name_owner_cancellable);
 
-	g_clear_object (&priv->proxy);
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->interface_removed_id);
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
+
+	nm_clear_g_cancellable (&priv->get_name_owner_cancellable);
+	nm_clear_g_cancellable (&priv->get_capabilities_cancellable);
 
 	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
+
+	g_clear_object (&priv->dbus_connection);
+
+	nm_clear_pointer (&priv->supp_ifaces, g_hash_table_destroy);
 }
 
 static void
@@ -610,5 +1362,11 @@ nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
 	object_class->dispose = dispose;
-}
 
+	signals[AVAILABLE_CHANGED] =
+	    g_signal_new (NM_SUPPLICANT_MANAGER_AVAILABLE_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
+}