mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-23 09:28:10 +02:00
https://bugzilla.redhat.com/show_bug.cgi?id=1823164 Fixes:b83f07916a('supplicant: large rework of wpa_supplicant handling') (cherry picked from commitfb023cc6e8)
1372 lines
47 KiB
C
1372 lines
47 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2006 - 2010 Red Hat, Inc.
|
|
* Copyright (C) 2007 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#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 "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 {
|
|
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 {
|
|
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)
|
|
|
|
NM_DEFINE_SINGLETON_GETTER (NMSupplicantManager, nm_supplicant_manager_get, NM_TYPE_SUPPLICANT_MANAGER);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define _NMLOG_DOMAIN LOGD_SUPPLICANT
|
|
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "supplicant", __VA_ARGS__)
|
|
|
|
/*****************************************************************************/
|
|
|
|
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, "???"),
|
|
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)
|
|
{
|
|
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,
|
|
NMTernary value)
|
|
{
|
|
priv->capabilities = NM_SUPPL_CAP_MASK_SET (priv->capabilities, type, value);
|
|
}
|
|
|
|
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 '?';
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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);
|
|
}
|
|
|
|
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 *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_object), result, &error);
|
|
if (!res)
|
|
_LOGD ("failed to set WFD IEs on wpa_supplicant: %s", error->message);
|
|
}
|
|
|
|
/**
|
|
* nm_supplicant_manager_set_wfd_ies:
|
|
* @self: the #NMSupplicantManager
|
|
* @wfd_ies: a #GBytes with the WFD IEs or %NULL
|
|
*
|
|
* This function sets the global WFD IEs on wpa_supplicant. Note that
|
|
* it would make more sense if this was per-device, but wpa_supplicant
|
|
* simply does not work that way.
|
|
* */
|
|
void
|
|
nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self,
|
|
GBytes *wfd_ies)
|
|
{
|
|
NMSupplicantManagerPrivate *priv;
|
|
GVariantBuilder params;
|
|
|
|
g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
|
|
|
|
priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
|
|
if (!priv->name_owner)
|
|
return;
|
|
|
|
_LOGD ("setting WFD IEs for P2P operation on %s", priv->name_owner->str);
|
|
|
|
g_variant_builder_init (¶ms, G_VARIANT_TYPE ("(ssv)"));
|
|
|
|
g_variant_builder_add (¶ms, "s", NM_WPAS_DBUS_INTERFACE);
|
|
g_variant_builder_add (¶ms, "s", "WFDIEs");
|
|
g_variant_builder_add_value (¶ms,
|
|
g_variant_new_variant (nm_utils_gbytes_to_variant_ay (wfd_ies)));
|
|
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
priv->name_owner->str,
|
|
NM_WPAS_DBUS_PATH,
|
|
DBUS_INTERFACE_PROPERTIES,
|
|
"Set",
|
|
g_variant_builder_end (¶ms),
|
|
G_VARIANT_TYPE ("()"),
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
3000,
|
|
NULL,
|
|
on_supplicant_wfd_ies_set,
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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,
|
|
int ifindex,
|
|
NMSupplicantDriver driver,
|
|
NMSupplicantManagerCreateInterfaceCb callback,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantManagerPrivate *priv;
|
|
NMSupplMgrCreateIfaceHandle *handle;
|
|
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), 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);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
_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);
|
|
}
|
|
|
|
NMSupplicantInterface *
|
|
nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self,
|
|
const char *object_path)
|
|
{
|
|
NMSupplicantManagerPrivate *priv;
|
|
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);
|
|
|
|
priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
|
|
iface_path = nm_ref_string_new (object_path);
|
|
|
|
supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path);
|
|
|
|
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 supp_iface;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_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 *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:
|
|
*
|
|
* commit 1634ac0654eba8d458640a115efc0a6cde3bac4d
|
|
* Author: Dan Williams <dcbw@redhat.com>
|
|
* Date: Sat Sep 29 19:06:30 2012 +0300
|
|
*
|
|
* dbus: Add global capabilities property
|
|
*/
|
|
_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 */
|
|
_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;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (res_key, "WFDIEs")) {
|
|
_caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_TRUE);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
_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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
_nm_supplicant_manager_unregister_interface (NMSupplicantManager *self,
|
|
NMSupplicantInterface *supp_iface)
|
|
{
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (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));
|
|
|
|
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
|
|
_supp_iface_add (NMSupplicantManager *self,
|
|
NMRefString *iface_path,
|
|
NMSupplicantInterface *supp_iface)
|
|
{
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
|
|
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
|
|
_supp_iface_remove_one (NMSupplicantManager *self,
|
|
NMSupplicantInterface *supp_iface,
|
|
gboolean force_remove_from_supplicant,
|
|
const char *reason)
|
|
{
|
|
#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);
|
|
|
|
nm_assert (c_list_is_empty (&supp_iface->supp_lst));
|
|
}
|
|
|
|
static void
|
|
_supp_iface_remove_all (NMSupplicantManager *self,
|
|
gboolean force_remove_from_supplicant,
|
|
const char *reason)
|
|
{
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
NMSupplicantInterface *supp_iface;
|
|
|
|
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
|
|
_available_reset_cb (gpointer user_data)
|
|
{
|
|
NMSupplicantManager *self = user_data;
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
|
|
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_changed (NMSupplicantManager *self,
|
|
const char *name_owner,
|
|
gboolean first_time)
|
|
{
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
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");
|
|
|
|
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). */
|
|
}
|
|
|
|
if (available_changed)
|
|
g_signal_emit (self, signals[AVAILABLE_CHANGED], 0);
|
|
}
|
|
|
|
static void
|
|
name_owner_changed_cb (GDBusConnection *connection,
|
|
const char *sender_name,
|
|
const char *object_path,
|
|
const char *interface_name,
|
|
const char *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
gs_unref_object NMSupplicantManager *self = g_object_ref (user_data);
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
const char *name_owner;
|
|
|
|
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 = user_data;
|
|
priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
|
|
g_clear_object (&priv->get_name_owner_cancellable);
|
|
|
|
name_owner_changed (self, nm_str_not_empty (name_owner), TRUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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->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
|
|
dispose (GObject *object)
|
|
{
|
|
NMSupplicantManager *self = (NMSupplicantManager *) object;
|
|
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
|
|
|
|
_supp_iface_remove_all (self, TRUE, "NMSupplicantManager is disposing");
|
|
|
|
nm_assert (c_list_is_empty (&priv->create_iface_lst_head));
|
|
|
|
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);
|
|
|
|
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
|
|
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);
|
|
}
|