mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-26 13:18:12 +02:00
The standard D-Bus PropertiesChanged signals are only in 1.0 and later, so we also have to listen to the deprecated signals for older supplicant versions. We can revert this commit when we drop support for wpa_supplicant 0.7.x.
1473 lines
49 KiB
C
1473 lines
49 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/* NetworkManager -- Network link manager
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright (C) 2006 - 2012 Red Hat, Inc.
|
|
* Copyright (C) 2006 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <glib.h>
|
|
|
|
#include "nm-supplicant-interface.h"
|
|
#include "nm-supplicant-manager.h"
|
|
#include "nm-logging.h"
|
|
#include "nm-marshal.h"
|
|
#include "nm-supplicant-config.h"
|
|
#include "nm-dbus-manager.h"
|
|
#include "nm-call-store.h"
|
|
#include "nm-dbus-glib-types.h"
|
|
#include "nm-glib-compat.h"
|
|
|
|
#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface"
|
|
#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS"
|
|
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
|
|
#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface"
|
|
#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists"
|
|
|
|
G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
|
|
|
|
static void wpas_iface_properties_changed (DBusGProxy *proxy,
|
|
GHashTable *props,
|
|
gpointer user_data);
|
|
|
|
static void wpas_iface_scan_done (DBusGProxy *proxy,
|
|
gboolean success,
|
|
gpointer user_data);
|
|
|
|
#define NM_SUPPLICANT_INTERFACE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
|
|
NM_TYPE_SUPPLICANT_INTERFACE, \
|
|
NMSupplicantInterfacePrivate))
|
|
|
|
/* Signals */
|
|
enum {
|
|
STATE, /* change in the interface's state */
|
|
REMOVED, /* interface was removed by the supplicant */
|
|
NEW_BSS, /* interface saw a new access point from a scan */
|
|
BSS_UPDATED, /* a BSS property changed */
|
|
SCAN_DONE, /* wifi scan is complete */
|
|
CONNECTION_ERROR, /* an error occurred during a connection request */
|
|
CREDENTIALS_REQUEST, /* 802.1x identity or password requested */
|
|
LAST_SIGNAL
|
|
};
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
/* Properties */
|
|
enum {
|
|
PROP_0 = 0,
|
|
PROP_SCANNING,
|
|
LAST_PROP
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
NMSupplicantManager * smgr;
|
|
gulong smgr_avail_id;
|
|
NMDBusManager * dbus_mgr;
|
|
char * dev;
|
|
gboolean is_wireless;
|
|
gboolean has_credreq; /* Whether querying 802.1x credentials is supported */
|
|
gboolean fast_supported;
|
|
|
|
char * object_path;
|
|
guint32 state;
|
|
NMCallStore * assoc_pcalls;
|
|
NMCallStore * other_pcalls;
|
|
|
|
gboolean scanning;
|
|
|
|
DBusGProxy * wpas_proxy;
|
|
DBusGProxy * iface_proxy;
|
|
DBusGProxy * props_proxy;
|
|
char * net_path;
|
|
guint32 blobs_left;
|
|
GHashTable * bss_proxies;
|
|
|
|
guint32 last_scan;
|
|
|
|
NMSupplicantConfig * cfg;
|
|
|
|
gboolean disposed;
|
|
} NMSupplicantInterfacePrivate;
|
|
|
|
/* FIXME: remove this and just store the standard D-Bus properties
|
|
* proxy object in bss_proxies when we drop support for wpa_supplicant
|
|
* 0.7.x.
|
|
*/
|
|
typedef struct {
|
|
/* Proxy for standard D-Bus Properties interface */
|
|
DBusGProxy *props;
|
|
/* Proxy for old wpa_supplicant-specific PropertiesChanged signal */
|
|
DBusGProxy *old_props;
|
|
} BssProxies;
|
|
|
|
static void
|
|
bss_proxies_free (gpointer data)
|
|
{
|
|
BssProxies *proxies = data;
|
|
|
|
g_object_unref (proxies->props);
|
|
g_object_unref (proxies->old_props);
|
|
memset (proxies, 0, sizeof (*proxies));
|
|
g_free (proxies);
|
|
}
|
|
|
|
static gboolean
|
|
cancel_all_cb (GObject *object, gpointer call_id, gpointer user_data)
|
|
{
|
|
dbus_g_proxy_cancel_call (DBUS_G_PROXY (object), (DBusGProxyCall *) call_id);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
cancel_all_callbacks (NMCallStore *store)
|
|
{
|
|
nm_call_store_foreach (store, NULL, cancel_all_cb, NULL);
|
|
nm_call_store_clear (store);
|
|
}
|
|
|
|
typedef struct {
|
|
NMSupplicantInterface *interface;
|
|
DBusGProxy *proxy;
|
|
NMCallStore *store;
|
|
DBusGProxyCall *call;
|
|
gboolean disposing;
|
|
} NMSupplicantInfo;
|
|
|
|
static NMSupplicantInfo *
|
|
nm_supplicant_info_new (NMSupplicantInterface *interface,
|
|
DBusGProxy *proxy,
|
|
NMCallStore *store)
|
|
{
|
|
NMSupplicantInfo *info;
|
|
|
|
info = g_slice_new0 (NMSupplicantInfo);
|
|
info->interface = g_object_ref (interface);
|
|
info->proxy = g_object_ref (proxy);
|
|
info->store = store;
|
|
|
|
return info;
|
|
}
|
|
|
|
static void
|
|
nm_supplicant_info_set_call (NMSupplicantInfo *info, DBusGProxyCall *call)
|
|
{
|
|
g_return_if_fail (info != NULL);
|
|
g_return_if_fail (call != NULL);
|
|
|
|
nm_call_store_add (info->store, G_OBJECT (info->proxy), (gpointer) call);
|
|
info->call = call;
|
|
}
|
|
|
|
static void
|
|
nm_supplicant_info_destroy (gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
|
|
/* Guard against double-disposal; since DBusGProxy doesn't guard against
|
|
* double-disposal, we could infinite loop here if we're in the middle of
|
|
* some wpa_supplicant D-Bus calls. When the supplicant dies we'll dispose
|
|
* of the proxy, which kills all its pending calls, which brings us here.
|
|
* Then when we unref the proxy here, its dispose() function will get called
|
|
* again, and we get right back here until we segfault because our callstack
|
|
* is too long.
|
|
*/
|
|
if (!info->disposing) {
|
|
info->disposing = TRUE;
|
|
|
|
if (info->call) {
|
|
nm_call_store_remove (info->store, G_OBJECT (info->proxy), info->call);
|
|
info->call = NULL;
|
|
}
|
|
|
|
g_object_unref (info->proxy);
|
|
info->proxy = NULL;
|
|
g_object_unref (info->interface);
|
|
info->interface = NULL;
|
|
|
|
memset (info, 0, sizeof (NMSupplicantInfo));
|
|
g_slice_free (NMSupplicantInfo, info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
emit_error_helper (NMSupplicantInterface *self,
|
|
GError *err)
|
|
{
|
|
const char *name = NULL;
|
|
|
|
if (err->domain == DBUS_GERROR && err->code == DBUS_GERROR_REMOTE_EXCEPTION)
|
|
name = dbus_g_error_get_name (err);
|
|
|
|
g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, err->message);
|
|
}
|
|
|
|
static void
|
|
signal_new_bss (NMSupplicantInterface *self,
|
|
const char *object_path,
|
|
GHashTable *props)
|
|
{
|
|
g_signal_emit (self, signals[NEW_BSS], 0, object_path, props);
|
|
}
|
|
|
|
static void
|
|
bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
GError *error = NULL;
|
|
GHashTable *props = NULL;
|
|
|
|
if (dbus_g_proxy_end_call (proxy, call_id, &error,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, &props,
|
|
G_TYPE_INVALID)) {
|
|
signal_new_bss (info->interface, dbus_g_proxy_get_path (proxy), props);
|
|
g_hash_table_destroy (props);
|
|
} else {
|
|
if (!strstr (error->message, "The BSSID requested was invalid")) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Couldn't retrieve BSSID properties: %s.",
|
|
error->message);
|
|
}
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bss_properties_changed (DBusGProxy *proxy,
|
|
const char *interface,
|
|
GHashTable *props,
|
|
const char **unused,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
|
|
if (g_strcmp0 (interface, WPAS_DBUS_IFACE_BSS) == 0)
|
|
g_signal_emit (self, signals[BSS_UPDATED], 0, dbus_g_proxy_get_path (proxy), props);
|
|
}
|
|
|
|
static void
|
|
old_bss_properties_changed (DBusGProxy *proxy,
|
|
GHashTable *props,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
|
|
g_signal_emit (self, signals[BSS_UPDATED], 0, dbus_g_proxy_get_path (proxy), props);
|
|
}
|
|
|
|
static void
|
|
handle_new_bss (NMSupplicantInterface *self,
|
|
const char *object_path,
|
|
GHashTable *props)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
NMSupplicantInfo *info;
|
|
DBusGProxyCall *call;
|
|
BssProxies *proxies;
|
|
|
|
g_return_if_fail (object_path != NULL);
|
|
|
|
if (g_hash_table_lookup (priv->bss_proxies, object_path))
|
|
return;
|
|
|
|
proxies = g_malloc0 (sizeof (*proxies));
|
|
proxies->props = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
|
|
WPAS_DBUS_SERVICE,
|
|
object_path,
|
|
DBUS_INTERFACE_PROPERTIES);
|
|
proxies->old_props = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
|
|
WPAS_DBUS_SERVICE,
|
|
object_path,
|
|
WPAS_DBUS_IFACE_BSS);
|
|
g_hash_table_insert (priv->bss_proxies,
|
|
(gpointer) dbus_g_proxy_get_path (proxies->props),
|
|
proxies);
|
|
|
|
/* Standard D-Bus PropertiesChanged signal */
|
|
dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED_BOXED,
|
|
G_TYPE_NONE,
|
|
G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_add_signal (proxies->props, "PropertiesChanged",
|
|
G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (proxies->props, "PropertiesChanged",
|
|
G_CALLBACK (bss_properties_changed),
|
|
self, NULL);
|
|
|
|
/* Old wpa_supplicant-specific PropertiesChanged signal; since it's using
|
|
* a different interface, we have to use a different DBusGProxy
|
|
*/
|
|
dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
|
|
G_TYPE_NONE,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_add_signal (proxies->old_props, "PropertiesChanged",
|
|
DBUS_TYPE_G_MAP_OF_VARIANT,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (proxies->old_props, "PropertiesChanged",
|
|
G_CALLBACK (old_bss_properties_changed),
|
|
self, NULL);
|
|
|
|
if (props) {
|
|
signal_new_bss (self, object_path, props);
|
|
} else {
|
|
info = nm_supplicant_info_new (self, proxies->props, priv->other_pcalls);
|
|
call = dbus_g_proxy_begin_call (proxies->props, "GetAll",
|
|
bssid_properties_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
G_TYPE_STRING, WPAS_DBUS_IFACE_BSS,
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wpas_iface_bss_added (DBusGProxy *proxy,
|
|
const char *object_path,
|
|
GHashTable *props,
|
|
gpointer user_data)
|
|
{
|
|
handle_new_bss (NM_SUPPLICANT_INTERFACE (user_data), object_path, props);
|
|
}
|
|
|
|
static void
|
|
wpas_iface_bss_removed (DBusGProxy *proxy,
|
|
const char *object_path,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
g_hash_table_remove (priv->bss_proxies, object_path);
|
|
}
|
|
|
|
static int
|
|
wpas_state_string_to_enum (const char *str_state)
|
|
{
|
|
if (!strcmp (str_state, "disconnected"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED;
|
|
else if (!strcmp (str_state, "inactive"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_INACTIVE;
|
|
else if (!strcmp (str_state, "scanning"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_SCANNING;
|
|
else if (!strcmp (str_state, "authenticating"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING;
|
|
else if (!strcmp (str_state, "associating"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING;
|
|
else if (!strcmp (str_state, "associated"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED;
|
|
else if (!strcmp (str_state, "4way_handshake"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE;
|
|
else if (!strcmp (str_state, "group_handshake"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE;
|
|
else if (!strcmp (str_state, "completed"))
|
|
return NM_SUPPLICANT_INTERFACE_STATE_COMPLETED;
|
|
|
|
nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant state '%s'", str_state);
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
set_state (NMSupplicantInterface *self, guint32 new_state)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
guint32 old_state = priv->state;
|
|
|
|
g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST);
|
|
|
|
if (new_state == priv->state)
|
|
return;
|
|
|
|
/* DOWN is a terminal state */
|
|
g_return_if_fail (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DOWN);
|
|
|
|
/* Cannot regress to READY, STARTING, or INIT from higher states */
|
|
if (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY)
|
|
g_return_if_fail (new_state > NM_SUPPLICANT_INTERFACE_STATE_READY);
|
|
|
|
if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
|
|
/* Cancel all pending calls when going down */
|
|
cancel_all_callbacks (priv->other_pcalls);
|
|
cancel_all_callbacks (priv->assoc_pcalls);
|
|
|
|
/* Disconnect supplicant manager state listeners since we're done */
|
|
if (priv->smgr_avail_id) {
|
|
g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id);
|
|
priv->smgr_avail_id = 0;
|
|
}
|
|
|
|
if (priv->iface_proxy) {
|
|
dbus_g_proxy_disconnect_signal (priv->iface_proxy,
|
|
"PropertiesChanged",
|
|
G_CALLBACK (wpas_iface_properties_changed),
|
|
self);
|
|
dbus_g_proxy_disconnect_signal (priv->iface_proxy,
|
|
"ScanDone",
|
|
G_CALLBACK (wpas_iface_scan_done),
|
|
self);
|
|
dbus_g_proxy_disconnect_signal (priv->iface_proxy,
|
|
"BSSAdded",
|
|
G_CALLBACK (wpas_iface_bss_added),
|
|
self);
|
|
dbus_g_proxy_disconnect_signal (priv->iface_proxy,
|
|
"BSSRemoved",
|
|
G_CALLBACK (wpas_iface_bss_removed),
|
|
self);
|
|
}
|
|
}
|
|
|
|
priv->state = new_state;
|
|
g_signal_emit (self, signals[STATE], 0, priv->state, old_state);
|
|
}
|
|
|
|
static void
|
|
set_state_from_string (NMSupplicantInterface *self, const char *new_state)
|
|
{
|
|
int state;
|
|
|
|
state = wpas_state_string_to_enum (new_state);
|
|
g_warn_if_fail (state > 0);
|
|
if (state > 0)
|
|
set_state (self, (guint32) state);
|
|
}
|
|
|
|
static void
|
|
set_scanning (NMSupplicantInterface *self, gboolean new_scanning)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
GTimeVal cur_time;
|
|
|
|
if (priv->scanning != new_scanning) {
|
|
priv->scanning = new_scanning;
|
|
|
|
/* Cache time of last scan completion */
|
|
if (priv->scanning == FALSE) {
|
|
g_get_current_time (&cur_time);
|
|
priv->last_scan = cur_time.tv_sec;
|
|
}
|
|
|
|
g_object_notify (G_OBJECT (self), "scanning");
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv;
|
|
|
|
g_return_val_if_fail (self != NULL, FALSE);
|
|
|
|
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
if (priv->scanning)
|
|
return TRUE;
|
|
if (priv->state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
wpas_iface_scan_done (DBusGProxy *proxy,
|
|
gboolean success,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
GTimeVal cur_time;
|
|
|
|
/* Cache last scan completed time */
|
|
g_get_current_time (&cur_time);
|
|
priv->last_scan = cur_time.tv_sec;
|
|
|
|
g_signal_emit (self, signals[SCAN_DONE], 0, success);
|
|
}
|
|
|
|
static void
|
|
wpas_iface_properties_changed (DBusGProxy *proxy,
|
|
GHashTable *props,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
GValue *value;
|
|
|
|
value = g_hash_table_lookup (props, "Scanning");
|
|
if (value && G_VALUE_HOLDS_BOOLEAN (value))
|
|
set_scanning (self, g_value_get_boolean (value));
|
|
|
|
value = g_hash_table_lookup (props, "State");
|
|
if (value && G_VALUE_HOLDS_STRING (value))
|
|
set_state_from_string (self, g_value_get_string (value));
|
|
|
|
value = g_hash_table_lookup (props, "BSSs");
|
|
if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) {
|
|
GPtrArray *paths = g_value_get_boxed (value);
|
|
int i;
|
|
|
|
for (i = 0; paths && (i < paths->len); i++)
|
|
handle_new_bss (self, g_ptr_array_index (paths, i), NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
iface_get_props_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
GHashTable *props = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (dbus_g_proxy_end_call (proxy, call_id, &error,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, &props,
|
|
G_TYPE_INVALID)) {
|
|
wpas_iface_properties_changed (NULL, props, info->interface);
|
|
g_hash_table_destroy (props);
|
|
} else {
|
|
nm_log_warn (LOGD_SUPPLICANT, "could not get interface properties: %s.",
|
|
error && error->message ? error->message : "(unknown)");
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wpas_iface_get_props (NMSupplicantInterface *self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
NMSupplicantInfo *info;
|
|
DBusGProxyCall *call;
|
|
|
|
info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->props_proxy, "GetAll",
|
|
iface_get_props_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
G_TYPE_STRING, WPAS_DBUS_IFACE_INTERFACE,
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
|
|
gboolean
|
|
nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self,
|
|
const char *field,
|
|
const char *value,
|
|
GError **error)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv;
|
|
|
|
g_return_val_if_fail (self != NULL, FALSE);
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
|
|
g_return_val_if_fail (field != NULL, FALSE);
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
g_return_val_if_fail (priv->has_credreq == TRUE, FALSE);
|
|
|
|
/* Need a network block object path */
|
|
g_return_val_if_fail (priv->net_path, FALSE);
|
|
return dbus_g_proxy_call_with_timeout (priv->iface_proxy, "NetworkReply",
|
|
5000,
|
|
error,
|
|
DBUS_TYPE_G_OBJECT_PATH, priv->net_path,
|
|
G_TYPE_STRING, field,
|
|
G_TYPE_STRING, value,
|
|
G_TYPE_INVALID);
|
|
}
|
|
|
|
static void
|
|
wpas_iface_network_request (DBusGProxy *proxy,
|
|
const char *object_path,
|
|
const char *field,
|
|
const char *message,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
g_return_if_fail (priv->has_credreq == TRUE);
|
|
g_return_if_fail (priv->net_path != NULL);
|
|
g_return_if_fail (g_strcmp0 (object_path, priv->net_path) == 0);
|
|
|
|
g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message);
|
|
}
|
|
|
|
static void
|
|
iface_check_netreply_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
|
|
GError *error = NULL;
|
|
|
|
if ( dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)
|
|
|| dbus_g_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs")) {
|
|
/* We know NetworkReply is supported if the NetworkReply method returned
|
|
* successfully (which is unexpected since we sent a bogus network
|
|
* object path) or if we got an "InvalidArgs" (which indicates NetworkReply
|
|
* is supported). We know it's not supported if we get an
|
|
* "UnknownMethod" error.
|
|
*/
|
|
priv->has_credreq = TRUE;
|
|
|
|
nm_log_dbg (LOGD_SUPPLICANT, "Supplicant %s network credentials requests",
|
|
priv->has_credreq ? "supports" : "does not support");
|
|
}
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
static void
|
|
wpas_iface_check_network_reply (NMSupplicantInterface *self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
NMSupplicantInfo *info;
|
|
DBusGProxyCall *call;
|
|
|
|
info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->iface_proxy, "NetworkReply",
|
|
iface_check_netreply_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
DBUS_TYPE_G_OBJECT_PATH, "/foobaraasdfasdf",
|
|
G_TYPE_STRING, "foobar",
|
|
G_TYPE_STRING, "foobar",
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
|
|
static void
|
|
interface_add_done (NMSupplicantInterface *self, char *path)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev);
|
|
|
|
priv->object_path = path;
|
|
|
|
priv->iface_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
|
|
WPAS_DBUS_SERVICE,
|
|
path,
|
|
WPAS_DBUS_IFACE_INTERFACE);
|
|
|
|
dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
|
|
G_TYPE_NONE,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_add_signal (priv->iface_proxy, "PropertiesChanged",
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (priv->iface_proxy, "PropertiesChanged",
|
|
G_CALLBACK (wpas_iface_properties_changed),
|
|
self, NULL);
|
|
|
|
dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone",
|
|
G_TYPE_BOOLEAN, G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanDone",
|
|
G_CALLBACK (wpas_iface_scan_done),
|
|
self,
|
|
NULL);
|
|
|
|
dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED,
|
|
G_TYPE_NONE,
|
|
DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded",
|
|
DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSAdded",
|
|
G_CALLBACK (wpas_iface_bss_added),
|
|
self,
|
|
NULL);
|
|
|
|
dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__STRING,
|
|
G_TYPE_NONE,
|
|
DBUS_TYPE_G_OBJECT_PATH,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_add_signal (priv->iface_proxy, "BSSRemoved",
|
|
DBUS_TYPE_G_OBJECT_PATH,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSRemoved",
|
|
G_CALLBACK (wpas_iface_bss_removed),
|
|
self,
|
|
NULL);
|
|
|
|
dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING,
|
|
G_TYPE_NONE,
|
|
DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_add_signal (priv->iface_proxy, "NetworkRequest",
|
|
DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING,
|
|
G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (priv->iface_proxy, "NetworkRequest",
|
|
G_CALLBACK (wpas_iface_network_request),
|
|
self,
|
|
NULL);
|
|
|
|
priv->props_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
|
|
WPAS_DBUS_SERVICE,
|
|
path,
|
|
DBUS_INTERFACE_PROPERTIES);
|
|
/* Get initial properties */
|
|
wpas_iface_get_props (self);
|
|
|
|
/* Check whether NetworkReply is supported */
|
|
wpas_iface_check_network_reply (self);
|
|
|
|
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_READY);
|
|
}
|
|
|
|
static void
|
|
interface_get_cb (DBusGProxy *proxy,
|
|
DBusGProxyCall *call_id,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
|
|
GError *error = NULL;
|
|
char *path = NULL;
|
|
|
|
if (dbus_g_proxy_end_call (proxy, call_id, &error,
|
|
DBUS_TYPE_G_OBJECT_PATH, &path,
|
|
G_TYPE_INVALID)) {
|
|
interface_add_done (info->interface, path);
|
|
} else {
|
|
nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s",
|
|
priv->dev, error->message);
|
|
g_clear_error (&error);
|
|
set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
|
|
}
|
|
}
|
|
|
|
static void
|
|
interface_get (NMSupplicantInterface *self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
NMSupplicantInfo *info;
|
|
DBusGProxyCall *call;
|
|
|
|
info = nm_supplicant_info_new (self, priv->wpas_proxy, priv->other_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->wpas_proxy, "GetInterface",
|
|
interface_get_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
G_TYPE_STRING, priv->dev,
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
|
|
static void
|
|
interface_add_cb (DBusGProxy *proxy,
|
|
DBusGProxyCall *call_id,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
|
|
GError *error = NULL;
|
|
char *path = NULL;
|
|
|
|
if (dbus_g_proxy_end_call (proxy, call_id, &error,
|
|
DBUS_TYPE_G_OBJECT_PATH, &path,
|
|
G_TYPE_INVALID)) {
|
|
interface_add_done (info->interface, path);
|
|
} else {
|
|
if (dbus_g_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) {
|
|
/* Interface already added, just get its object path */
|
|
interface_get (info->interface);
|
|
} else if ( g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)
|
|
|| g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_EXEC_FAILED)
|
|
|| g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FORK_FAILED)
|
|
|| g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FAILED)
|
|
|| dbus_g_error_has_name (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
|
|
/* Supplicant wasn't running and could be launched via service
|
|
* activation. Wait for it to start by moving back to the INIT
|
|
* state.
|
|
*/
|
|
nm_log_dbg (LOGD_SUPPLICANT, "(%s): failed to activate supplicant: %s",
|
|
priv->dev, error->message);
|
|
set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_INIT);
|
|
} else {
|
|
nm_log_err (LOGD_SUPPLICANT, "(%s): error adding interface: %s",
|
|
priv->dev, error->message);
|
|
set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
|
|
}
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
interface_add (NMSupplicantInterface *self, gboolean is_wireless)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
DBusGProxyCall *call;
|
|
NMSupplicantInfo *info;
|
|
GHashTable *hash;
|
|
GValue *driver, *ifname;
|
|
|
|
/* Can only start the interface from INIT state */
|
|
g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT);
|
|
|
|
nm_log_dbg (LOGD_SUPPLICANT, "(%s): adding interface to supplicant", priv->dev);
|
|
|
|
/* Move to starting to prevent double-calls of interface_add() */
|
|
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
|
|
|
|
/* Try to add the interface to the supplicant. If the supplicant isn't
|
|
* running, this will start it via D-Bus activation and return the response
|
|
* when the supplicant has started.
|
|
*/
|
|
|
|
info = nm_supplicant_info_new (self, priv->wpas_proxy, priv->other_pcalls);
|
|
|
|
hash = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
driver = g_new0 (GValue, 1);
|
|
g_value_init (driver, G_TYPE_STRING);
|
|
g_value_set_string (driver, is_wireless ? "nl80211,wext" : "wired");
|
|
g_hash_table_insert (hash, "Driver", driver);
|
|
|
|
ifname = g_new0 (GValue, 1);
|
|
g_value_init (ifname, G_TYPE_STRING);
|
|
g_value_set_string (ifname, priv->dev);
|
|
g_hash_table_insert (hash, "Ifname", ifname);
|
|
|
|
call = dbus_g_proxy_begin_call (priv->wpas_proxy, "CreateInterface",
|
|
interface_add_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, hash,
|
|
G_TYPE_INVALID);
|
|
|
|
g_hash_table_destroy (hash);
|
|
g_value_unset (driver);
|
|
g_free (driver);
|
|
g_value_unset (ifname);
|
|
g_free (ifname);
|
|
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
|
|
static void
|
|
smgr_avail_cb (NMSupplicantManager *smgr,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data);
|
|
|
|
if (nm_supplicant_manager_available (smgr)) {
|
|
/* This can happen if the supplicant couldn't be activated but
|
|
* for some reason was started after the activation failure.
|
|
*/
|
|
if (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT)
|
|
interface_add (self, priv->is_wireless);
|
|
} else {
|
|
/* The supplicant stopped; so we must tear down the interface */
|
|
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
|
|
}
|
|
}
|
|
|
|
static void
|
|
remove_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
|
|
nm_log_dbg (LOGD_SUPPLICANT, "Couldn't remove network from supplicant interface: %s.",
|
|
error && error->message ? error->message : "(unknown)");
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
disconnect_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Couldn't disconnect supplicant interface: %s.",
|
|
error && error->message ? error->message : "(unknown)");
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
|
|
void
|
|
nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv;
|
|
|
|
g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
|
|
|
|
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
/* Clear and cancel all pending calls related to a prior
|
|
* connection attempt.
|
|
*/
|
|
cancel_all_callbacks (priv->assoc_pcalls);
|
|
|
|
/* Don't do anything if there is no connection to the supplicant yet. */
|
|
if (!priv->iface_proxy)
|
|
return;
|
|
|
|
/* Disconnect from the current AP */
|
|
if ( (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
|
|
&& (priv->state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)) {
|
|
dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect",
|
|
disconnect_cb,
|
|
NULL, NULL,
|
|
G_TYPE_INVALID);
|
|
}
|
|
|
|
/* Remove any network that was added by NetworkManager */
|
|
if (priv->net_path) {
|
|
dbus_g_proxy_begin_call (priv->iface_proxy, "RemoveNetwork",
|
|
remove_network_cb,
|
|
NULL, NULL,
|
|
DBUS_TYPE_G_OBJECT_PATH, priv->net_path,
|
|
G_TYPE_INVALID);
|
|
g_free (priv->net_path);
|
|
priv->net_path = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
select_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
GError *err = NULL;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Couldn't select network config: %s.", err->message);
|
|
emit_error_helper (info->interface, err);
|
|
g_error_free (err);
|
|
}
|
|
}
|
|
|
|
static void
|
|
call_select_network (NMSupplicantInterface *self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
DBusGProxyCall *call;
|
|
NMSupplicantInfo *info;
|
|
|
|
/* We only select the network after all blobs (if any) have been set */
|
|
if (priv->blobs_left > 0)
|
|
return;
|
|
|
|
info = nm_supplicant_info_new (self, priv->iface_proxy, priv->assoc_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->iface_proxy, "SelectNetwork",
|
|
select_network_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
DBUS_TYPE_G_OBJECT_PATH, priv->net_path,
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
|
|
static void
|
|
add_blob_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
|
|
GError *err = NULL;
|
|
guint tmp;
|
|
|
|
priv->blobs_left--;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network certificates: %s.", err->message);
|
|
emit_error_helper (info->interface, err);
|
|
g_error_free (err);
|
|
} else
|
|
call_select_network (info->interface);
|
|
}
|
|
|
|
static void
|
|
add_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
|
|
GError *err = NULL;
|
|
GHashTable *blobs;
|
|
GHashTableIter iter;
|
|
gpointer name, data;
|
|
DBusGProxyCall *call;
|
|
NMSupplicantInfo *blob_info;
|
|
|
|
g_free (priv->net_path);
|
|
priv->net_path = NULL;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &err,
|
|
DBUS_TYPE_G_OBJECT_PATH, &priv->net_path,
|
|
G_TYPE_INVALID)) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Couldn't add a network to the supplicant interface: %s.",
|
|
err->message);
|
|
emit_error_helper (info->interface, err);
|
|
g_error_free (err);
|
|
return;
|
|
}
|
|
|
|
/* Send blobs first; otherwise jump to sending the config settings */
|
|
blobs = nm_supplicant_config_get_blobs (priv->cfg);
|
|
priv->blobs_left = g_hash_table_size (blobs);
|
|
g_hash_table_iter_init (&iter, blobs);
|
|
while (g_hash_table_iter_next (&iter, &name, &data)) {
|
|
blob_info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->iface_proxy, "AddBlob",
|
|
add_blob_cb,
|
|
blob_info,
|
|
nm_supplicant_info_destroy,
|
|
DBUS_TYPE_STRING, name,
|
|
DBUS_TYPE_G_UCHAR_ARRAY, blobs,
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (blob_info, call);
|
|
}
|
|
|
|
call_select_network (info->interface);
|
|
}
|
|
|
|
static void
|
|
set_ap_scan_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
|
|
GError *err = NULL;
|
|
DBusGProxyCall *call;
|
|
GHashTable *config_hash;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Couldn't send AP scan mode to the supplicant interface: %s.",
|
|
err->message);
|
|
emit_error_helper (info->interface, err);
|
|
g_error_free (err);
|
|
return;
|
|
}
|
|
|
|
nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d",
|
|
nm_supplicant_config_get_ap_scan (priv->cfg));
|
|
|
|
info = nm_supplicant_info_new (info->interface, priv->iface_proxy, info->store);
|
|
config_hash = nm_supplicant_config_get_hash (priv->cfg);
|
|
call = dbus_g_proxy_begin_call (priv->iface_proxy, "AddNetwork",
|
|
add_network_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, config_hash,
|
|
G_TYPE_INVALID);
|
|
g_hash_table_destroy (config_hash);
|
|
nm_supplicant_info_set_call (info, call);
|
|
}
|
|
|
|
gboolean
|
|
nm_supplicant_interface_set_config (NMSupplicantInterface * self,
|
|
NMSupplicantConfig * cfg)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv;
|
|
NMSupplicantInfo *info;
|
|
DBusGProxyCall *call;
|
|
GValue value = { 0, };
|
|
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
|
|
|
|
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
nm_supplicant_interface_disconnect (self);
|
|
|
|
/* Make sure the supplicant supports EAP-FAST before trying to send
|
|
* it an EAP-FAST configuration.
|
|
*/
|
|
if (nm_supplicant_config_fast_required (cfg) && !priv->fast_supported) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "EAP-FAST is not supported by the supplicant");
|
|
return FALSE;
|
|
}
|
|
|
|
if (priv->cfg)
|
|
g_object_unref (priv->cfg);
|
|
priv->cfg = cfg;
|
|
|
|
if (cfg == NULL)
|
|
return TRUE;
|
|
|
|
g_object_ref (priv->cfg);
|
|
|
|
g_value_init (&value, G_TYPE_UINT);
|
|
g_value_set_uint (&value, nm_supplicant_config_get_ap_scan (priv->cfg));
|
|
|
|
info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->props_proxy, "Set",
|
|
set_ap_scan_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
G_TYPE_STRING, WPAS_DBUS_IFACE_INTERFACE,
|
|
G_TYPE_STRING, "ApScan",
|
|
G_TYPE_VALUE, &value,
|
|
G_TYPE_INVALID);
|
|
nm_supplicant_info_set_call (info, call);
|
|
|
|
g_value_unset (&value);
|
|
return call != NULL;
|
|
}
|
|
|
|
static void
|
|
scan_request_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
|
|
{
|
|
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
|
|
GError *err = NULL;
|
|
|
|
if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) {
|
|
nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", err->message);
|
|
}
|
|
g_signal_emit (info->interface, signals[SCAN_DONE], 0, err ? FALSE : TRUE);
|
|
g_clear_error (&err);
|
|
}
|
|
|
|
static void
|
|
destroy_gvalue (gpointer data)
|
|
{
|
|
GValue *value = (GValue *) data;
|
|
|
|
g_value_unset (value);
|
|
g_slice_free (GValue, value);
|
|
}
|
|
|
|
static GValue *
|
|
string_to_gvalue (const char *str)
|
|
{
|
|
GValue *val = g_slice_new0 (GValue);
|
|
|
|
g_value_init (val, G_TYPE_STRING);
|
|
g_value_set_string (val, str);
|
|
return val;
|
|
}
|
|
|
|
gboolean
|
|
nm_supplicant_interface_request_scan (NMSupplicantInterface * self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv;
|
|
NMSupplicantInfo *info;
|
|
DBusGProxyCall *call;
|
|
GHashTable *hash;
|
|
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
|
|
|
|
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
/* Scan parameters */
|
|
hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue);
|
|
g_hash_table_insert (hash, "Type", string_to_gvalue ("active"));
|
|
|
|
info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
|
|
call = dbus_g_proxy_begin_call (priv->iface_proxy, "Scan",
|
|
scan_request_cb,
|
|
info,
|
|
nm_supplicant_info_destroy,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, hash,
|
|
G_TYPE_INVALID);
|
|
g_hash_table_destroy (hash);
|
|
nm_supplicant_info_set_call (info, call);
|
|
|
|
return call != NULL;
|
|
}
|
|
|
|
guint32
|
|
nm_supplicant_interface_get_state (NMSupplicantInterface * self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_STATE_DOWN);
|
|
|
|
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state;
|
|
}
|
|
|
|
const char *
|
|
nm_supplicant_interface_state_to_string (guint32 state)
|
|
{
|
|
switch (state) {
|
|
case NM_SUPPLICANT_INTERFACE_STATE_INIT:
|
|
return "init";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_STARTING:
|
|
return "starting";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_READY:
|
|
return "ready";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
|
|
return "disconnected";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE:
|
|
return "inactive";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_SCANNING:
|
|
return "scanning";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING:
|
|
return "authenticating";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING:
|
|
return "associating";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED:
|
|
return "associated";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE:
|
|
return "4-way handshake";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE:
|
|
return "group handshake";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
|
|
return "completed";
|
|
case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
|
|
return "down";
|
|
default:
|
|
break;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
nm_supplicant_interface_get_device (NMSupplicantInterface * self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
|
|
|
|
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
|
|
}
|
|
|
|
const char *
|
|
nm_supplicant_interface_get_object_path (NMSupplicantInterface *self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
|
|
|
|
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->object_path;
|
|
}
|
|
|
|
const char *
|
|
nm_supplicant_interface_get_ifname (NMSupplicantInterface *self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
|
|
|
|
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
NMSupplicantInterface *
|
|
nm_supplicant_interface_new (NMSupplicantManager *smgr,
|
|
const char *ifname,
|
|
gboolean is_wireless,
|
|
gboolean fast_supported,
|
|
gboolean start_now)
|
|
{
|
|
NMSupplicantInterface *self;
|
|
NMSupplicantInterfacePrivate *priv;
|
|
guint id;
|
|
|
|
g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (smgr), NULL);
|
|
g_return_val_if_fail (ifname != NULL, NULL);
|
|
|
|
self = g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, NULL);
|
|
if (self) {
|
|
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
|
|
priv->smgr = g_object_ref (smgr);
|
|
id = g_signal_connect (priv->smgr,
|
|
"notify::" NM_SUPPLICANT_MANAGER_AVAILABLE,
|
|
G_CALLBACK (smgr_avail_cb),
|
|
self);
|
|
priv->smgr_avail_id = id;
|
|
|
|
priv->dev = g_strdup (ifname);
|
|
priv->is_wireless = is_wireless;
|
|
priv->fast_supported = fast_supported;
|
|
|
|
if (start_now)
|
|
interface_add (self, priv->is_wireless);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
static void
|
|
nm_supplicant_interface_init (NMSupplicantInterface * self)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
|
|
DBusGConnection *bus;
|
|
|
|
priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
|
|
priv->assoc_pcalls = nm_call_store_new ();
|
|
priv->other_pcalls = nm_call_store_new ();
|
|
priv->dbus_mgr = nm_dbus_manager_get ();
|
|
|
|
bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
|
|
priv->wpas_proxy = dbus_g_proxy_new_for_name (bus,
|
|
WPAS_DBUS_SERVICE,
|
|
WPAS_DBUS_PATH,
|
|
WPAS_DBUS_INTERFACE);
|
|
|
|
priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bss_proxies_free);
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id) {
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id) {
|
|
case PROP_SCANNING:
|
|
g_value_set_boolean (value, NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object)->scanning);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
|
|
|
|
if (priv->disposed) {
|
|
G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
|
|
return;
|
|
}
|
|
priv->disposed = TRUE;
|
|
|
|
/* Cancel pending calls before unrefing the dbus manager */
|
|
cancel_all_callbacks (priv->other_pcalls);
|
|
nm_call_store_destroy (priv->other_pcalls);
|
|
|
|
cancel_all_callbacks (priv->assoc_pcalls);
|
|
nm_call_store_destroy (priv->assoc_pcalls);
|
|
|
|
if (priv->props_proxy)
|
|
g_object_unref (priv->props_proxy);
|
|
|
|
if (priv->iface_proxy)
|
|
g_object_unref (priv->iface_proxy);
|
|
|
|
g_free (priv->net_path);
|
|
|
|
if (priv->wpas_proxy)
|
|
g_object_unref (priv->wpas_proxy);
|
|
|
|
g_hash_table_destroy (priv->bss_proxies);
|
|
|
|
if (priv->smgr) {
|
|
if (priv->smgr_avail_id)
|
|
g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id);
|
|
g_object_unref (priv->smgr);
|
|
}
|
|
|
|
g_free (priv->dev);
|
|
|
|
if (priv->dbus_mgr)
|
|
g_object_unref (priv->dbus_mgr);
|
|
|
|
if (priv->cfg)
|
|
g_object_unref (priv->cfg);
|
|
|
|
g_free (priv->object_path);
|
|
|
|
/* Chain up to the parent class */
|
|
G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (object_class, sizeof (NMSupplicantInterfacePrivate));
|
|
|
|
object_class->dispose = dispose;
|
|
object_class->set_property = set_property;
|
|
object_class->get_property = get_property;
|
|
|
|
/* Properties */
|
|
g_object_class_install_property (object_class, PROP_SCANNING,
|
|
g_param_spec_boolean ("scanning",
|
|
"Scanning",
|
|
"Scanning",
|
|
FALSE,
|
|
G_PARAM_READABLE));
|
|
|
|
/* Signals */
|
|
signals[STATE] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_STATE,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, state),
|
|
NULL, NULL,
|
|
_nm_marshal_VOID__UINT_UINT,
|
|
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
|
|
|
|
signals[REMOVED] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_REMOVED,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, removed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[NEW_BSS] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_NEW_BSS,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, new_bss),
|
|
NULL, NULL,
|
|
_nm_marshal_VOID__STRING_POINTER,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
|
|
|
|
signals[BSS_UPDATED] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, bss_updated),
|
|
NULL, NULL,
|
|
_nm_marshal_VOID__STRING_POINTER,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
|
|
|
|
signals[SCAN_DONE] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_done),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__BOOLEAN,
|
|
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
|
|
|
signals[CONNECTION_ERROR] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_error),
|
|
NULL, NULL,
|
|
_nm_marshal_VOID__STRING_STRING,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
|
|
|
|
signals[CREDENTIALS_REQUEST] =
|
|
g_signal_new (NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, credentials_request),
|
|
NULL, NULL,
|
|
_nm_marshal_VOID__STRING_STRING,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
|
|
}
|
|
|