static gboolean
is_available (NMSupplicantManager *self)
{
	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE);

	if (die_count_exceeded (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->die_count))
		return FALSE;
	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->running;
}
void
nm_supplicant_manager_iface_release (NMSupplicantManager *self,
                                     NMSupplicantInterface *iface)
{
	NMSupplicantManagerPrivate *priv;
	const char *ifname, *op;

	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));

	ifname = nm_supplicant_interface_get_ifname (iface);
	g_assert (ifname);

	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);

	g_return_if_fail (g_hash_table_lookup (priv->ifaces, ifname) == iface);

	/* Ask wpa_supplicant to remove this interface */
	op = nm_supplicant_interface_get_object_path (iface);
	if (priv->running && priv->proxy && op) {
		dbus_g_proxy_call_no_reply (priv->proxy, "RemoveInterface",
			                        DBUS_TYPE_G_OBJECT_PATH, op,
			                        G_TYPE_INVALID);
	}

	g_hash_table_remove (priv->ifaces, ifname);
}
NMSupplicantInterface *
nm_supplicant_manager_iface_get (NMSupplicantManager * self,
                                 const char *ifname,
                                 gboolean is_wireless)
{
	NMSupplicantManagerPrivate *priv;
	NMSupplicantInterface *iface = NULL;
	gboolean start_now;

	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
	g_return_val_if_fail (ifname != NULL, NULL);

	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);

	iface = g_hash_table_lookup (priv->ifaces, ifname);
	if (!iface) {
		/* 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.
		 */
		start_now = !die_count_exceeded (priv->die_count);

		nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
		iface = nm_supplicant_interface_new (self, ifname, is_wireless, start_now);
		if (iface)
			g_hash_table_insert (priv->ifaces, g_strdup (ifname), iface);
	} else {
		nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname);
	}

	return iface;
}
static void
dispose (GObject *object)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);

	if (priv->disposed)
		goto out;
	priv->disposed = TRUE;

	if (priv->die_count_reset_id)
		g_source_remove (priv->die_count_reset_id);

	if (priv->dbus_mgr) {
		if (priv->name_owner_id)
			g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
		g_object_unref (G_OBJECT (priv->dbus_mgr));
	}

	g_hash_table_destroy (priv->ifaces);

	if (priv->proxy)
		g_object_unref (priv->proxy);

out:
	/* Chain up to the parent class */
	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
}
static void
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);

	if (priv->cancellable) {
		g_cancellable_cancel (priv->cancellable);
		g_clear_object (&priv->cancellable);
	}

	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;
	}

	g_clear_object (&priv->proxy);

	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
}
void
nm_supplicant_manager_iface_release (NMSupplicantManager *self,
                                     NMSupplicantInterface *iface)
{
	NMSupplicantManagerPrivate *priv;
	const char *ifname, *op;

	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));

	ifname = nm_supplicant_interface_get_ifname (iface);
	g_assert (ifname);

	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);

	g_return_if_fail (g_hash_table_lookup (priv->ifaces, ifname) == iface);

	/* Ask wpa_supplicant to remove this interface */
	op = nm_supplicant_interface_get_object_path (iface);
	if (priv->running && priv->proxy && op) {
		g_dbus_proxy_call (priv->proxy,
		                   "RemoveInterface",
		                   g_variant_new ("(o)", op),
		                   G_DBUS_CALL_FLAGS_NONE,
		                   3000,
		                   NULL,
		                   NULL,
		                   NULL);
	}

	g_hash_table_remove (priv->ifaces, ifname);
}
static gboolean
is_available (NMSupplicantManager *self)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);

	return    priv->running
	       && !die_count_exceeded (priv->die_count);
}
static void
update_capabilities (NMSupplicantManager *self)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	NMSupplicantInterface *iface;
	GHashTableIter hash_iter;
	const char **array;
	GVariant *value;

	/* The supplicant only advertises global capabilities if the following
	 * commit has been applied:
	 *
	 * commit 1634ac0654eba8d458640a115efc0a6cde3bac4d
	 * Author: Dan Williams <*****@*****.**>
	 * Date:   Sat Sep 29 19:06:30 2012 +0300
	 *
	 * dbus: Add global capabilities property
	 */
	priv->ap_support = AP_SUPPORT_UNKNOWN;

	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 = AP_SUPPORT_NO;
			if (_nm_utils_string_in_list ("ap", array))
				priv->ap_support = AP_SUPPORT_YES;
			g_free (array);
		}
		g_variant_unref (value);
	}

	/* Tell all interfaces about results of the AP check */
	g_hash_table_iter_init (&hash_iter, priv->ifaces);
	while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &iface))
		nm_supplicant_interface_set_ap_support (iface, priv->ap_support);

	nm_log_dbg (LOGD_SUPPLICANT, "AP mode is %ssupported",
	            (priv->ap_support == AP_SUPPORT_YES) ? "" :
	                (priv->ap_support == AP_SUPPORT_NO) ? "not " : "possibly ");

	/* EAP-FAST */
	priv->fast_supported = FALSE;
	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 (_nm_utils_string_in_list ("fast", array))
				priv->fast_supported = TRUE;
			g_free (array);
		}
		g_variant_unref (value);
	}

	nm_log_dbg (LOGD_SUPPLICANT, "EAP-FAST is %ssupported", priv->fast_supported ? "" : "not ");
}
static void
set_running (NMSupplicantManager *self, gboolean now_running)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	gboolean old_available = nm_supplicant_manager_available (self);

	priv->running = now_running;
	if (old_available != nm_supplicant_manager_available (self))
		g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE);
}
static void
set_die_count (NMSupplicantManager *self, guint new_die_count)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	gboolean old_available = nm_supplicant_manager_available (self);

	priv->die_count = new_die_count;
	if (old_available != nm_supplicant_manager_available (self))
		g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE);
}
static gboolean
wpas_die_count_reset_cb (gpointer user_data)
{
	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (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);
	nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant die count reset");
	return FALSE;
}
static void
set_die_count (NMSupplicantManager *self, guint new_die_count)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	gboolean old_available = is_available (self);
	gboolean new_available;

	priv->die_count = new_die_count;
	new_available = is_available (self);
	if (old_available != new_available)
		availability_changed (self, new_available);
}
static void
set_running (NMSupplicantManager *self, gboolean now_running)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	gboolean old_available = is_available (self);
	gboolean new_available;

	priv->running = now_running;
	new_available = is_available (self);
	if (old_available != new_available)
		availability_changed (self, new_available);
}
static void
availability_changed (NMSupplicantManager *self, gboolean available)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	GList *ifaces, *iter;

	/* priv->ifaces may be modified if availability changes; can't use GHashTableIter */
	ifaces = g_hash_table_get_values (priv->ifaces);
	for (iter = ifaces; iter; iter = iter->next)
		nm_supplicant_interface_set_supplicant_available (NM_SUPPLICANT_INTERFACE (iter->data), available);
	g_list_free (ifaces);
}
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);
}
static void
availability_changed (NMSupplicantManager *self, gboolean available)
{
	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);
}
static void
dispose (GObject *object)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);

	if (priv->die_count_reset_id) {
		g_source_remove (priv->die_count_reset_id);
		priv->die_count_reset_id = 0;
	}

	if (priv->cancellable) {
		g_cancellable_cancel (priv->cancellable);
		g_clear_object (&priv->cancellable);
	}

	g_clear_pointer (&priv->ifaces, g_hash_table_unref);
	g_clear_object (&priv->proxy);

	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
}
static void
name_owner_changed (NMDBusManager *dbus_mgr,
                    const char *name,
                    const char *old_owner,
                    const char *new_owner,
                    gpointer user_data)
{
	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	gboolean old_owner_good = (old_owner && strlen (old_owner));
	gboolean new_owner_good = (new_owner && strlen (new_owner));

	/* We only care about the supplicant here */
	if (strcmp (WPAS_DBUS_SERVICE, name) != 0)
		return;

	if (!old_owner_good && new_owner_good) {
		nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant started");
		set_running (self, TRUE);
	} else if (old_owner_good && !new_owner_good) {
		nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant stopped");

		/* 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)) {
			nm_log_info (LOGD_SUPPLICANT,
			             "wpa_supplicant die count %d; ignoring for 10 seconds",
			             priv->die_count);
		}

		set_running (self, FALSE);
	}
}
static void
nm_supplicant_manager_init (NMSupplicantManager * self)
{
	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
	DBusGConnection *bus;

	priv->dbus_mgr = nm_dbus_manager_get ();
	priv->name_owner_id = g_signal_connect (priv->dbus_mgr,
	                                        NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
	                                        G_CALLBACK (name_owner_changed),
	                                        self);
	priv->running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, WPAS_DBUS_SERVICE);

	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
	priv->proxy = dbus_g_proxy_new_for_name (bus,
	                                         WPAS_DBUS_SERVICE,
	                                         WPAS_DBUS_PATH,
	                                         WPAS_DBUS_INTERFACE);

	priv->ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
/**
 * 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).
 *
 * Retruns: (transfer-full): returns a #NMSupplicantInterface or %NULL.
 *   Must be unrefed at the end.
 * */
NMSupplicantInterface *
nm_supplicant_manager_create_interface (NMSupplicantManager *self,
                                        const char *ifname,
                                        gboolean is_wireless)
{
	NMSupplicantManagerPrivate *priv;
	NMSupplicantInterface *iface;
	GSList *ifaces;

	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
	g_return_val_if_fail (ifname != NULL, NULL);

	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);

	nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);

	/* 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);
	}

	iface = nm_supplicant_interface_new (ifname,
	                                     is_wireless,
	                                     priv->fast_supported,
	                                     priv->ap_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);

	return iface;
}
static void
name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
{
	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);
	nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant %s", owner ? "running" : "stopped");

	if (owner) {
		set_running (self, TRUE);
		update_capabilities (self);
	} 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)) {
			nm_log_info (LOGD_SUPPLICANT,
			             "wpa_supplicant die count %d; ignoring for 10 seconds",
			             priv->die_count);
		}

		set_running (self, FALSE);

		priv->fast_supported = FALSE;
	}

	g_free (owner);
}
static void
_sup_iface_last_ref (gpointer data,
                     GObject *object,
                     gboolean is_last_ref)
{
	NMSupplicantManager *self = data;
	NMSupplicantManagerPrivate *priv;
	NMSupplicantInterface *sup_iface = (NMSupplicantInterface *) object;
	const char *op;

	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);

	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);
	}

	priv->ifaces = g_slist_remove (priv->ifaces, sup_iface);
	g_object_remove_toggle_ref ((GObject *) sup_iface, _sup_iface_last_ref, self);
}
static void
on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data)
{
	NMSupplicantManager *self;
	NMSupplicantManagerPrivate *priv;
	GError *error = NULL;
	GDBusProxy *proxy;

	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
	if (!proxy) {
		nm_log_warn (LOGD_SUPPLICANT,
		             "Failed to acquire wpa_supplicant proxy: Wi-Fi and 802.1x will not be available (%s)",
		             error->message);
		g_clear_error (&error);
		return;
	}

	self = NM_SUPPLICANT_MANAGER (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);
}