summaryrefslogtreecommitdiff
path: root/src/supplicant/nm-supplicant-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplicant/nm-supplicant-manager.c')
-rw-r--r--src/supplicant/nm-supplicant-manager.c1403
1 files changed, 1105 insertions, 298 deletions
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c
index 08bed98cf3..cf5cf11163 100644
--- a/src/supplicant/nm-supplicant-manager.c
+++ b/src/supplicant/nm-supplicant-manager.c
@@ -8,20 +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"
/*****************************************************************************/
-typedef struct {
- GDBusProxy *proxy;
+#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 {
+ 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 die_count_reset_id;
- guint die_count;
- bool running:1;
+
+ 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 {
@@ -37,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
@@ -48,6 +94,27 @@ NM_CACHED_QUARK_FCN ("nm-supplicant-error-quark", nm_supplicant_error_quark)
/*****************************************************************************/
+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, "???"),
@@ -58,6 +125,39 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_driver_to_string, NMSupplicantDriver,
/*****************************************************************************/
+NMTernary
+nm_supplicant_manager_is_available (NMSupplicantManager *self)
+{
+ g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NM_TERNARY_FALSE);
+
+ return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->available;
+}
+
+NMRefString *
+nm_supplicant_manager_get_dbus_name_owner (NMSupplicantManager *self)
+{
+ g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
+
+ 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
_caps_set (NMSupplicantManagerPrivate *priv,
NMSupplCapType type,
@@ -66,82 +166,68 @@ _caps_set (NMSupplicantManagerPrivate *priv,
priv->capabilities = NM_SUPPL_CAP_MASK_SET (priv->capabilities, type, value);
}
-static const char *
-_caps_to_str (NMSupplicantManagerPrivate *priv,
- NMSupplCapType type)
+static char
+_caps_to_char (NMSupplicantManagerPrivate *priv,
+ NMSupplCapType type)
{
NMTernary val;
- val = NM_SUPPL_CAP_MASK_GET (priv->capabilities, type);;
+ val = NM_SUPPL_CAP_MASK_GET (priv->capabilities, type);
if (val == NM_TERNARY_TRUE)
- return "supported";
+ return '+';
if (val == NM_TERNARY_FALSE)
- return "not supported";
- return "possibly supported";
+ return '-';
+ return '?';
}
/*****************************************************************************/
-static gboolean
-die_count_exceeded (guint32 count)
+static void
+_dbus_call_remove_interface (GDBusConnection *dbus_connection,
+ const char *name_owner,
+ const char *iface_path)
{
- return count > 2;
+ 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);
}
-static gboolean
-is_available (NMSupplicantManager *self)
+void
+_nm_supplicant_manager_dbus_call_remove_interface (NMSupplicantManager *self,
+ const char *name_owner,
+ const char *iface_path)
{
- NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-
- return priv->running
- && !die_count_exceeded (priv->die_count);
+ _dbus_call_remove_interface (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_connection,
+ name_owner,
+ iface_path);
}
/*****************************************************************************/
static void
-_sup_iface_last_ref (gpointer data,
- GObject *object,
- gboolean is_last_ref)
-{
- NMSupplicantManager *self = data;
- NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- NMSupplicantInterface *sup_iface = NM_SUPPLICANT_INTERFACE (object);
- const char *op;
-
- nm_assert (is_last_ref);
- nm_assert (c_list_contains (&priv->supp_lst_head, &sup_iface->supp_lst));
-
- c_list_unlink (&sup_iface->supp_lst);
-
- 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);
- }
-
- g_object_remove_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self);
-}
-
-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);
}
/**
@@ -164,138 +250,672 @@ nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self,
priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- _LOGD ("setting WFD IEs for P2P operation");
+ if (!priv->name_owner)
+ return;
+
+ _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 (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);
+ } 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 *sup_iface;
+ 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;
+ }
+
+ 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;
+ }
- c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) {
- if (nm_streq0 (nm_supplicant_interface_get_ifname (sup_iface), ifname))
- g_return_val_if_reached (NULL);
+ 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;
}
- sup_iface = nm_supplicant_interface_new (ifname,
- NULL,
- driver,
- priv->capabilities);
+ _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);
- c_list_link_tail (&priv->supp_lst_head, &sup_iface->supp_lst);
- g_object_add_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self);
+ _create_iface_dbus_start (self, handle);
+ return handle;
+}
- /* 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 (sup_iface, TRUE);
+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);
- return sup_iface;
+ 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 *sup_iface;
+ 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);
- c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) {
- if (nm_streq0 (nm_supplicant_interface_get_object_path (sup_iface), object_path))
- g_return_val_if_reached (NULL);
- }
+ supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path);
- sup_iface = nm_supplicant_interface_new (NULL,
- object_path,
- NM_SUPPLICANT_DRIVER_WIRELESS,
- priv->capabilities);
+ if (supp_iface)
+ return g_object_ref (supp_iface);
- c_list_link_tail (&priv->supp_lst_head, &sup_iface->supp_lst);
- g_object_add_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self);
+ supp_iface = nm_supplicant_interface_new (self,
+ iface_path,
+ 0,
+ NM_SUPPLICANT_DRIVER_UNKNOWN);
- /* 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 (sup_iface, TRUE);
+ _supp_iface_add (self, iface_path, supp_iface);
- return sup_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);
- NMSupplicantInterface *sup_iface;
- 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:
@@ -315,220 +935,389 @@ update_capabilities (NMSupplicantManager *self)
_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);
-
- 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);
- _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) {
- if (g_strv_contains (array, "ap")) _caps_set (priv, NM_SUPPL_CAP_TYPE_AP, NM_TERNARY_TRUE);
- if (g_strv_contains (array, "pmf")) _caps_set (priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_TRUE);
- if (g_strv_contains (array, "fils")) _caps_set (priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_TRUE);
- if (g_strv_contains (array, "p2p")) _caps_set (priv, NM_SUPPL_CAP_TYPE_P2P, NM_TERNARY_TRUE);
- if (g_strv_contains (array, "ft")) _caps_set (priv, NM_SUPPL_CAP_TYPE_FT, NM_TERNARY_TRUE);
- if (g_strv_contains (array, "sha384")) _caps_set (priv, NM_SUPPL_CAP_TYPE_SHA384, NM_TERNARY_TRUE);
- if (g_strv_contains (array, "mesh")) _caps_set (priv, NM_SUPPL_CAP_TYPE_MESH, NM_TERNARY_TRUE);
- g_free (array);
+ _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);
- }
-
- _caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_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) {
- const char **a;
-
- for (a = array; *a; a++) {
- if (g_ascii_strcasecmp (*a, "FAST") == 0) {
- _caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_TRUE);
- 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);
}
- _caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_FALSE);
- value = g_dbus_proxy_get_cached_property (priv->proxy, "WFDIEs");
- if (value) {
- _caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_TRUE);
- g_variant_unref (value);
- }
-
- _LOGD ("AP mode is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_AP));
- _LOGD ("PMF is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_PMF));
- _LOGD ("FILS is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_FILS));
- _LOGD ("P2P is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_P2P));
- _LOGD ("FT is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_FT));
- _LOGD ("SHA384 is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_SHA384));
- _LOGD ("Mesh is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_MESH));
- _LOGD ("EAP-FAST is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_FAST));
- _LOGD ("WFD is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_WFD));
-
- c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) {
- nm_supplicant_interface_set_global_capabilities (sup_iface,
- priv->capabilities);
- }
+ _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);
}
-static void
-availability_changed (NMSupplicantManager *self, gboolean available)
+/*****************************************************************************/
+
+void
+_nm_supplicant_manager_unregister_interface (NMSupplicantManager *self,
+ NMSupplicantInterface *supp_iface)
{
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- gs_unref_ptrarray GPtrArray *sup_ifaces = NULL;
- NMSupplicantInterface *sup_iface;
- gsize i, n;
- n = c_list_length (&priv->supp_lst_head);
- if (n == 0)
- return;
+ 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));
- /* 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. */
+ 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 ();
+}
- sup_ifaces = g_ptr_array_new_full (n, g_object_unref);
- c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst)
- g_ptr_array_add (sup_ifaces, g_object_ref (sup_iface));
+static void
+_supp_iface_add (NMSupplicantManager *self,
+ NMRefString *iface_path,
+ NMSupplicantInterface *supp_iface)
+{
+ NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- for (i = 0; i < n; i++)
- nm_supplicant_interface_set_supplicant_available (sup_ifaces->pdata[i], available);
+ 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);
}
+ }
- priv->capabilities = NM_SUPPL_CAP_MASK_NONE;
+ _supp_iface_remove_all (self, TRUE, "name-owner changed");
- set_running (self, FALSE);
+ 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;
+
+ /* 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);
+}
+
+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 = NM_SUPPLICANT_MANAGER (user_data);
+ 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);
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->cancellable = g_cancellable_new ();
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_WPAS_DBUS_SERVICE,
- NM_WPAS_DBUS_PATH,
- NM_WPAS_DBUS_INTERFACE,
- priv->cancellable,
- (GAsyncReadyCallback) on_proxy_acquired,
- self);
+ 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
@@ -536,20 +1325,32 @@ dispose (GObject *object)
{
NMSupplicantManager *self = (NMSupplicantManager *) object;
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- NMSupplicantInterface *sup_iface;
- 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));
- while ((sup_iface = c_list_first_entry (&priv->supp_lst_head, NMSupplicantInterface, supp_lst))) {
- c_list_unlink (&sup_iface->supp_lst);
- g_object_remove_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self);
- }
+ 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
@@ -558,5 +1359,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);
+}