diff options
| author | Michael Biebl <biebl@debian.org> | 2017-01-17 20:25:09 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-01-17 20:25:09 +0100 |
| commit | 58f8be580039b0575b197b9573a1c92745d96d30 (patch) | |
| tree | 2c226233f623a0dcb529be0eb8cdf97e4a2ae0c0 /src/supplicant/nm-supplicant-manager.c | |
| parent | 45cb5bb3c0e6edb887cf69b417fcaf7053814a9b (diff) | |
New upstream version 1.5.90 upstream/1.5.90
Diffstat (limited to 'src/supplicant/nm-supplicant-manager.c')
| -rw-r--r-- | src/supplicant/nm-supplicant-manager.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c new file mode 100644 index 00000000..2fbfa391 --- /dev/null +++ b/src/supplicant/nm-supplicant-manager.c @@ -0,0 +1,413 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include "nm-default.h" + +#include "nm-supplicant-manager.h" + +#include <string.h> + +#include "nm-supplicant-interface.h" +#include "nm-supplicant-types.h" +#include "nm-core-internal.h" + +/*****************************************************************************/ + +typedef struct { + GDBusProxy * proxy; + GCancellable * cancellable; + gboolean running; + + GSList *ifaces; + gboolean fast_supported; + NMSupplicantFeature ap_support; + guint die_count_reset_id; + guint die_count; +} NMSupplicantManagerPrivate; + +struct _NMSupplicantManager { + GObject parent; + NMSupplicantManagerPrivate _priv; +}; + +struct _NMSupplicantManagerClass { + GObjectClass parent; +}; + +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) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_SUPPLICANT +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "supplicant", __VA_ARGS__) + +/*****************************************************************************/ + +G_DEFINE_QUARK (nm-supplicant-error-quark, nm_supplicant_error); + +/*****************************************************************************/ + +static inline gboolean +die_count_exceeded (guint32 count) +{ + return count > 2; +} + +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 +_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); +} + +/** + * 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 * +nm_supplicant_manager_create_interface (NMSupplicantManager *self, + const char *ifname, + NMSupplicantDriver driver) +{ + 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); + + _LOGD ("(%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, + driver, + 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 +update_capabilities (NMSupplicantManager *self) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + GSList *ifaces; + const char **array; + GVariant *value; + + /* The supplicant only advertises global capabilities if the following + * commit has been applied: + * + * commit 1634ac0654eba8d458640a115efc0a6cde3bac4d + * Author: Dan Williams <dcbw@redhat.com> + * Date: Sat Sep 29 19:06:30 2012 +0300 + * + * dbus: Add global capabilities property + */ + priv->ap_support = NM_SUPPLICANT_FEATURE_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 = NM_SUPPLICANT_FEATURE_NO; + if (array) { + if (g_strv_contains (array, "ap")) + priv->ap_support = NM_SUPPLICANT_FEATURE_YES; + g_free (array); + } + } + g_variant_unref (value); + } + + /* Tell all interfaces about results of the AP check */ + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) + nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support); + + _LOGD ("AP mode is %ssupported", + (priv->ap_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->ap_support == NM_SUPPLICANT_FEATURE_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 (array) { + if (g_strv_contains (array, "fast")) + priv->fast_supported = TRUE; + g_free (array); + } + } + g_variant_unref (value); + } + + _LOGD ("EAP-FAST is %ssupported", priv->fast_supported ? "" : "not "); +} + +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 +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 +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 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); + _LOGI ("wpa_supplicant die count reset"); + return FALSE; +} + +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); + _LOGI ("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)) { + _LOGI ("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 +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) { + _LOGW ("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); +} + +/*****************************************************************************/ + +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); +} + +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); +} + +static void +nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dispose; +} + |