NetworkManager/src/supplicant/nm-supplicant-interface.c
Thomas Haller 5a03de7051 wifi: introduce enum type NMSupplicantInterfaceState instead of plain int
Also change the signature of the NM_SUPPLICANT_INTERFACE_STATE signal,
to have three "int" type arguments. Thereby also fix the subscribers
to this signal that wrongly had type guint32, instead of guint
(which happens to be the same underlying type, so no real problem).

https://mail.gnome.org/archives/networkmanager-list/2017-February/msg00021.html
2017-02-14 09:47:05 +01:00

1590 lines
54 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 "nm-default.h"
#include "nm-supplicant-interface.h"
#include <stdio.h>
#include <string.h>
#include "NetworkManagerUtils.h"
#include "nm-supplicant-config.h"
#include "nm-core-internal.h"
#include "nm-dbus-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"
static NM_CACHED_QUARK_FCN ("bss-proxy-inited", bss_proxy_inited_quark)
/*****************************************************************************/
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 */
BSS_REMOVED, /* supplicant removed BSS from its scan list */
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 };
NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface,
PROP_IFACE,
PROP_SCANNING,
PROP_CURRENT_BSS,
PROP_DRIVER,
PROP_FAST_SUPPORT,
PROP_AP_SUPPORT,
);
typedef struct {
char * dev;
NMSupplicantDriver driver;
gboolean has_credreq; /* Whether querying 802.1x credentials is supported */
NMSupplicantFeature fast_support;
NMSupplicantFeature ap_support; /* Lightweight AP mode support */
guint32 max_scan_ssids;
guint32 ready_count;
char * object_path;
NMSupplicantInterfaceState state;
int disconnect_reason;
gboolean scanning;
GDBusProxy * wpas_proxy;
GCancellable * init_cancellable;
GDBusProxy * iface_proxy;
GCancellable * other_cancellable;
GCancellable * assoc_cancellable;
char * net_path;
guint32 blobs_left;
GHashTable * bss_proxies;
char * current_bss;
gint32 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */
NMSupplicantConfig *cfg;
} NMSupplicantInterfacePrivate;
struct _NMSupplicantInterface {
GObject parent;
NMSupplicantInterfacePrivate _priv;
};
struct _NMSupplicantInterfaceClass {
GObjectClass parent;
};
G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
#define NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSupplicantInterface, NM_IS_SUPPLICANT_INTERFACE)
/*****************************************************************************/
#define _NMLOG_DOMAIN LOGD_SUPPLICANT
#define _NMLOG_PREFIX_NAME "sup-iface"
#define _NMLOG(level, ...) \
G_STMT_START { \
char _sbuf[64]; \
\
nm_log ((level), _NMLOG_DOMAIN, \
"%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG_PREFIX_NAME, \
((self) \
? nm_sprintf_buf (_sbuf, \
"[%p,%s]", \
(self), \
NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev) \
: "") \
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} G_STMT_END
/*****************************************************************************/
static void
emit_error_helper (NMSupplicantInterface *self, GError *error)
{
char *name = NULL;
if (g_dbus_error_is_remote_error (error))
name = g_dbus_error_get_remote_error (error);
g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, error->message);
g_free (name);
}
static void
bss_props_changed_cb (GDBusProxy *proxy,
GVariant *changed_properties,
char **invalidated_properties,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (priv->scanning)
priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
g_signal_emit (self, signals[BSS_UPDATED], 0,
g_dbus_proxy_get_object_path (proxy),
changed_properties);
}
static GVariant *
_get_bss_proxy_properties (NMSupplicantInterface *self, GDBusProxy *proxy)
{
gs_strfreev char **properties = NULL;
GVariantBuilder builder;
char **iter;
iter = properties = g_dbus_proxy_get_cached_property_names (proxy);
if (!iter)
return NULL;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
while (*iter) {
GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter);
g_variant_builder_add (&builder, "{sv}", *iter++, copy);
g_variant_unref (copy);
}
return g_variant_builder_end (&builder);
}
static void
on_bss_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *props = NULL;
if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
self = NM_SUPPLICANT_INTERFACE (user_data);
_LOGD ("failed to acquire BSS proxy: (%s)", error->message);
g_hash_table_remove (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->bss_proxies,
g_dbus_proxy_get_object_path (proxy));
}
return;
}
self = NM_SUPPLICANT_INTERFACE (user_data);
props = _get_bss_proxy_properties (self, proxy);
if (!props)
return;
g_object_set_qdata (G_OBJECT (proxy), bss_proxy_inited_quark (), GUINT_TO_POINTER (TRUE));
g_signal_emit (self, signals[NEW_BSS], 0,
g_dbus_proxy_get_object_path (proxy),
g_variant_ref_sink (props));
}
static void
handle_new_bss (NMSupplicantInterface *self, const char *object_path)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
GDBusProxy *bss_proxy;
g_return_if_fail (object_path != NULL);
if (g_hash_table_lookup (priv->bss_proxies, object_path))
return;
bss_proxy = g_object_new (G_TYPE_DBUS_PROXY,
"g-bus-type", G_BUS_TYPE_SYSTEM,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-name", WPAS_DBUS_SERVICE,
"g-object-path", object_path,
"g-interface-name", WPAS_DBUS_IFACE_BSS,
NULL);
g_hash_table_insert (priv->bss_proxies,
(char *) g_dbus_proxy_get_object_path (bss_proxy),
bss_proxy);
g_signal_connect (bss_proxy, "g-properties-changed", G_CALLBACK (bss_props_changed_cb), self);
g_async_initable_init_async (G_ASYNC_INITABLE (bss_proxy),
G_PRIORITY_DEFAULT,
priv->other_cancellable,
(GAsyncReadyCallback) on_bss_proxy_acquired,
self);
}
static void
set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
NMSupplicantInterfaceState old_state = priv->state;
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_READY) {
if (priv->other_cancellable) {
g_warn_if_fail (priv->other_cancellable == NULL);
g_cancellable_cancel (priv->other_cancellable);
g_clear_object (&priv->other_cancellable);
}
priv->other_cancellable = g_cancellable_new ();
} else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
if (priv->init_cancellable)
g_cancellable_cancel (priv->init_cancellable);
g_clear_object (&priv->init_cancellable);
if (priv->other_cancellable)
g_cancellable_cancel (priv->other_cancellable);
g_clear_object (&priv->other_cancellable);
if (priv->iface_proxy)
g_signal_handlers_disconnect_by_data (priv->iface_proxy, self);
}
priv->state = new_state;
if ( priv->state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING
|| old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
/* Disconnect reason is no longer relevant when not in the DISCONNECTED state */
if (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED)
priv->disconnect_reason = 0;
g_signal_emit (self, signals[STATE], 0,
(int) priv->state,
(int) old_state,
(int) priv->disconnect_reason);
}
static NMSupplicantInterfaceState
wpas_state_string_to_enum (const char *str_state)
{
if (!strcmp (str_state, "interface_disabled"))
return NM_SUPPLICANT_INTERFACE_STATE_DISABLED;
else 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;
return NM_SUPPLICANT_INTERFACE_STATE_INVALID;
}
static void
set_state_from_string (NMSupplicantInterface *self, const char *new_state)
{
NMSupplicantInterfaceState state;
state = wpas_state_string_to_enum (new_state);
if (state == NM_SUPPLICANT_INTERFACE_STATE_INVALID) {
_LOGW ("unknown supplicant state '%s'", new_state);
return;
}
set_state (self, state);
}
static void
set_scanning (NMSupplicantInterface *self, gboolean new_scanning)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (priv->scanning != new_scanning) {
priv->scanning = new_scanning;
/* Cache time of last scan completion */
if (priv->scanning == FALSE)
priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
_notify (self, PROP_SCANNING);
}
}
gboolean
nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv;
g_return_val_if_fail (self, 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;
}
const char *
nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv;
g_return_val_if_fail (self != NULL, FALSE);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
return priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY ? priv->current_bss : NULL;
}
gint32
nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self)
{
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->last_scan;
}
#define MATCH_PROPERTY(p, n, v, t) (!strcmp (p, n) && g_variant_is_of_type (v, t))
static void
parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
gboolean have_active = FALSE, have_ssid = FALSE;
gint32 max_scan_ssids = -1;
const char **array;
g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT));
if ( g_variant_lookup (capabilities, "Scan", "^a&s", &array)
&& array) {
if (g_strv_contains (array, "active"))
have_active = TRUE;
if (g_strv_contains (array, "ssid"))
have_ssid = TRUE;
g_free (array);
}
if (g_variant_lookup (capabilities, "MaxScanSSID", "i", &max_scan_ssids)) {
/* We need active scan and SSID probe capabilities to care about MaxScanSSIDs */
if (max_scan_ssids > 0 && have_active && have_ssid) {
/* wpa_supplicant's WPAS_MAX_SCAN_SSIDS value is 16, but for speed
* and to ensure we don't disclose too many SSIDs from the hidden
* list, we'll limit to 5.
*/
priv->max_scan_ssids = CLAMP (max_scan_ssids, 0, 5);
_LOGI ("supports %d scan SSIDs", priv->max_scan_ssids);
}
}
}
static void
iface_check_ready (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (priv->ready_count && priv->state < NM_SUPPLICANT_INTERFACE_STATE_READY) {
priv->ready_count--;
if (priv->ready_count == 0)
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_READY);
}
}
gboolean
nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self,
const char *field,
const char *value,
GError **error)
{
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *reply = NULL;
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);
reply = g_dbus_proxy_call_sync (priv->iface_proxy,
"NetworkReply",
g_variant_new ("(oss)",
priv->net_path,
field,
value),
G_DBUS_CALL_FLAGS_NONE,
5000,
NULL,
error);
if (error && *error)
g_dbus_error_strip_remote_error (*error);
return !!reply;
}
static void
iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *variant = NULL;
gs_free_error GError *error = NULL;
/* 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.
*/
variant = g_dbus_proxy_call_finish (proxy, result, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (variant || _nm_dbus_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs"))
priv->has_credreq = TRUE;
_LOGD ("supplicant %s network credentials requests",
priv->has_credreq ? "supports" : "does not support");
iface_check_ready (self);
}
NMSupplicantFeature
nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self)
{
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->ap_support;
}
void
nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
NMSupplicantFeature ap_support)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
/* Use the best indicator of support between the supplicant global
* Capabilities property and the interface's introspection data.
*/
if (ap_support > priv->ap_support)
priv->ap_support = ap_support;
}
void
nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self,
NMSupplicantFeature fast_support)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
priv->fast_support = fast_support;
}
static void
iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *variant = NULL;
gs_free_error GError *error = NULL;
const char *data;
variant = _nm_dbus_proxy_call_finish (proxy, result,
G_VARIANT_TYPE ("(s)"),
&error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (variant) {
g_variant_get (variant, "(&s)", &data);
/* The ProbeRequest method only exists if AP mode has been enabled */
if (strstr (data, "ProbeRequest"))
priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
}
iface_check_ready (self);
}
static void
wpas_iface_scan_done (GDBusProxy *proxy,
gboolean success,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
GVariant *props;
GHashTableIter iter;
char *bss_path;
GDBusProxy *bss_proxy;
/* Cache last scan completed time */
priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
/* Emit NEW_BSS so that wifi device has the APs (in case it removed them) */
g_hash_table_iter_init (&iter, priv->bss_proxies);
while (g_hash_table_iter_next (&iter, (gpointer) &bss_path, (gpointer) &bss_proxy)) {
if (g_object_get_qdata (G_OBJECT (bss_proxy), bss_proxy_inited_quark ())) {
props = _get_bss_proxy_properties (self, bss_proxy);
if (props) {
g_signal_emit (self, signals[NEW_BSS], 0,
bss_path,
g_variant_ref_sink (props));
g_variant_unref (props);
}
}
}
g_signal_emit (self, signals[SCAN_DONE], 0, success);
}
static void
wpas_iface_bss_added (GDBusProxy *proxy,
const char *path,
GVariant *props,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (priv->scanning)
priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
handle_new_bss (self, path);
}
static void
wpas_iface_bss_removed (GDBusProxy *proxy,
const char *path,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
g_signal_emit (self, signals[BSS_REMOVED], 0, path);
g_hash_table_remove (priv->bss_proxies, path);
}
static void
wpas_iface_network_request (GDBusProxy *proxy,
const char *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);
if (priv->has_credreq && priv->net_path && !g_strcmp0 (path, priv->net_path))
g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message);
}
static void
props_changed_cb (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
const char *s, **array, **iter;
gboolean b = FALSE;
gint32 i32;
GVariant *v;
g_object_freeze_notify (G_OBJECT (self));
if (g_variant_lookup (changed_properties, "Scanning", "b", &b))
set_scanning (self, b);
if ( g_variant_lookup (changed_properties, "State", "&s", &s)
&& priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY) {
/* Only transition to actual wpa_supplicant interface states (ie,
* anything > READY) after the NMSupplicantInterface has had a
* chance to initialize, which is signalled by entering the READY
* state.
*/
set_state_from_string (self, s);
}
if (g_variant_lookup (changed_properties, "BSSs", "^a&o", &array)) {
iter = array;
while (*iter)
handle_new_bss (self, *iter++);
g_free (array);
}
if (g_variant_lookup (changed_properties, "CurrentBSS", "&o", &s)) {
if (strcmp (s, "/") == 0)
s = NULL;
if (g_strcmp0 (s, priv->current_bss) != 0) {
g_free (priv->current_bss);
priv->current_bss = g_strdup (s);
_notify (self, PROP_CURRENT_BSS);
}
}
v = g_variant_lookup_value (changed_properties, "Capabilities", G_VARIANT_TYPE_VARDICT);
if (v) {
parse_capabilities (self, v);
g_variant_unref (v);
}
if (g_variant_lookup (changed_properties, "DisconnectReason", "i", &i32)) {
/* Disconnect reason is currently only given for deauthentication events,
* not disassociation; currently they are IEEE 802.11 "reason codes",
* defined by (IEEE 802.11-2007, 7.3.1.7, Table 7-22). Any locally caused
* deauthentication will be negative, while authentications caused by the
* AP will be positive.
*/
priv->disconnect_reason = i32;
if (priv->disconnect_reason != 0)
_LOGW ("connection disconnected (reason %d)", priv->disconnect_reason);
}
g_object_thaw_notify (G_OBJECT (self));
}
static void
on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_free_error GError *error = NULL;
if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
self = NM_SUPPLICANT_INTERFACE (user_data);
_LOGW ("failed to acquire wpa_supplicant interface proxy: (%s)", error->message);
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
return;
}
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
_nm_dbus_signal_connect (priv->iface_proxy, "ScanDone", G_VARIANT_TYPE ("(b)"),
G_CALLBACK (wpas_iface_scan_done), self);
_nm_dbus_signal_connect (priv->iface_proxy, "BSSAdded", G_VARIANT_TYPE ("(oa{sv})"),
G_CALLBACK (wpas_iface_bss_added), self);
_nm_dbus_signal_connect (priv->iface_proxy, "BSSRemoved", G_VARIANT_TYPE ("(o)"),
G_CALLBACK (wpas_iface_bss_removed), self);
_nm_dbus_signal_connect (priv->iface_proxy, "NetworkRequest", G_VARIANT_TYPE ("(oss)"),
G_CALLBACK (wpas_iface_network_request), self);
/* Scan result aging parameters */
g_dbus_proxy_call (priv->iface_proxy,
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
WPAS_DBUS_IFACE_INTERFACE,
"BSSExpireAge",
g_variant_new_uint32 (250)),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
NULL,
NULL);
g_dbus_proxy_call (priv->iface_proxy,
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
WPAS_DBUS_IFACE_INTERFACE,
"BSSExpireCount",
g_variant_new_uint32 (2)),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
NULL,
NULL);
/* Check whether NetworkReply and AP mode are supported */
priv->ready_count = 1;
g_dbus_proxy_call (priv->iface_proxy,
"NetworkReply",
g_variant_new ("(oss)",
"/fff",
"foobar",
"foobar"),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
(GAsyncReadyCallback) iface_check_netreply_cb,
self);
if (priv->ap_support == NM_SUPPLICANT_FEATURE_UNKNOWN) {
/* If the global supplicant capabilities property is not present, we can
* fall back to checking whether the ProbeRequest method is supported. If
* neither of these works we have no way of determining if AP mode is
* supported or not. hostap 1.0 and earlier don't support either of these.
*/
priv->ready_count++;
g_dbus_proxy_call (priv->iface_proxy,
DBUS_INTERFACE_INTROSPECTABLE ".Introspect",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
(GAsyncReadyCallback) iface_introspect_cb,
self);
}
}
static void
interface_add_done (NMSupplicantInterface *self, const char *path)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
_LOGD ("interface added to supplicant");
priv->object_path = g_strdup (path);
priv->iface_proxy = g_object_new (G_TYPE_DBUS_PROXY,
"g-bus-type", G_BUS_TYPE_SYSTEM,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-name", WPAS_DBUS_SERVICE,
"g-object-path", priv->object_path,
"g-interface-name", WPAS_DBUS_IFACE_INTERFACE,
NULL);
g_signal_connect (priv->iface_proxy, "g-properties-changed", G_CALLBACK (props_changed_cb), self);
g_async_initable_init_async (G_ASYNC_INITABLE (priv->iface_proxy),
G_PRIORITY_DEFAULT,
priv->init_cancellable,
(GAsyncReadyCallback) on_iface_proxy_acquired,
self);
}
static void
interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *variant = NULL;
gs_free_error GError *error = NULL;
const char *path;
variant = _nm_dbus_proxy_call_finish (proxy, result,
G_VARIANT_TYPE ("(o)"),
&error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (variant) {
g_variant_get (variant, "(&o)", &path);
interface_add_done (self, path);
} else {
g_dbus_error_strip_remote_error (error);
_LOGE ("error getting interface: %s", error->message);
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
}
static void
interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *variant = NULL;
const char *path;
variant = _nm_dbus_proxy_call_finish (proxy, result,
G_VARIANT_TYPE ("(o)"),
&error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (variant) {
g_variant_get (variant, "(&o)", &path);
interface_add_done (self, path);
} else if (_nm_dbus_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) {
/* Interface already added, just get its object path */
g_dbus_proxy_call (priv->wpas_proxy,
"GetInterface",
g_variant_new ("(s)", priv->dev),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
(GAsyncReadyCallback) interface_get_cb,
self);
} else if ( g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_EXEC_FAILED)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FORK_FAILED)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FAILED)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMEOUT)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NO_REPLY)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMED_OUT)
|| g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
/* Supplicant wasn't running and could not be launched via service
* activation. Wait for it to start by moving back to the INIT
* state.
*/
g_dbus_error_strip_remote_error (error);
_LOGD ("failed to activate supplicant: %s", error->message);
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_INIT);
} else {
g_dbus_error_strip_remote_error (error);
_LOGE ("error adding interface: %s", error->message);
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
}
#if HAVE_WEXT
#define DEFAULT_WIFI_DRIVER "nl80211,wext"
#else
#define DEFAULT_WIFI_DRIVER "nl80211"
#endif
static void
on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_free_error GError *error = NULL;
GDBusProxy *wpas_proxy;
GVariantBuilder props;
const char *driver_name = NULL;
wpas_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
if (!wpas_proxy) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
self = NM_SUPPLICANT_INTERFACE (user_data);
_LOGW ("failed to acquire wpa_supplicant proxy: (%s)", error->message);
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
return;
}
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
priv->wpas_proxy = wpas_proxy;
/* 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.
*/
switch (priv->driver) {
case NM_SUPPLICANT_DRIVER_WIRELESS:
driver_name = DEFAULT_WIFI_DRIVER;
break;
case NM_SUPPLICANT_DRIVER_WIRED:
driver_name = "wired";
break;
case NM_SUPPLICANT_DRIVER_MACSEC:
driver_name = "macsec_linux";
break;
}
g_return_if_fail (driver_name);
g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&props, "{sv}",
"Driver",
g_variant_new_string (driver_name));
g_variant_builder_add (&props, "{sv}",
"Ifname",
g_variant_new_string (priv->dev));
g_dbus_proxy_call (priv->wpas_proxy,
"CreateInterface",
g_variant_new ("(a{sv})", &props),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
(GAsyncReadyCallback) interface_add_cb,
self);
}
static void
interface_add (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
/* Can only start the interface from INIT state */
g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT);
_LOGD ("adding interface to supplicant");
/* Move to starting to prevent double-calls of interface_add() */
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
g_warn_if_fail (priv->init_cancellable == NULL);
g_clear_object (&priv->init_cancellable);
priv->init_cancellable = g_cancellable_new ();
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
NULL,
WPAS_DBUS_SERVICE,
WPAS_DBUS_PATH,
WPAS_DBUS_INTERFACE,
priv->init_cancellable,
(GAsyncReadyCallback) on_wpas_proxy_acquired,
self);
}
void
nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self,
gboolean available)
{
NMSupplicantInterfacePrivate *priv;
g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (available) {
/* 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);
} else {
/* The supplicant stopped; so we must tear down the interface */
set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
}
static void
log_result_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
gs_unref_variant GVariant *reply = NULL;
gs_free_error GError *error = NULL;
reply = g_dbus_proxy_call_finish (proxy, result, &error);
if ( !reply
&& !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
&& !strstr (error->message, "fi.w1.wpa_supplicant1.NotConnected")) {
g_dbus_error_strip_remote_error (error);
nm_log_warn (_NMLOG_DOMAIN, "%s: failed to %s: %s",
_NMLOG_PREFIX_NAME, (const char *) user_data, error->message);
}
}
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);
/* Cancel all pending calls related to a prior connection attempt */
if (priv->assoc_cancellable) {
g_cancellable_cancel (priv->assoc_cancellable);
g_clear_object (&priv->assoc_cancellable);
}
/* 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)) {
g_dbus_proxy_call (priv->iface_proxy,
"Disconnect",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) log_result_cb,
"disconnect");
}
/* Remove any network that was added by NetworkManager */
if (priv->net_path) {
g_dbus_proxy_call (priv->iface_proxy,
"RemoveNetwork",
g_variant_new ("(o)", priv->net_path),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->other_cancellable,
(GAsyncReadyCallback) log_result_cb,
"remove network");
g_free (priv->net_path);
priv->net_path = NULL;
}
}
static void
select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
gs_unref_variant GVariant *reply = NULL;
gs_free_error GError *error = NULL;
reply = g_dbus_proxy_call_finish (proxy, result, &error);
if ( !reply
&& !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
self = NM_SUPPLICANT_INTERFACE (user_data);
g_dbus_error_strip_remote_error (error);
_LOGW ("couldn't select network config: %s", error->message);
emit_error_helper (self, error);
}
}
static void
call_select_network (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
/* We only select the network after all blobs (if any) have been set */
if (priv->blobs_left == 0) {
g_dbus_proxy_call (priv->iface_proxy,
"SelectNetwork",
g_variant_new ("(o)", priv->net_path),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->assoc_cancellable,
(GAsyncReadyCallback) select_network_cb,
self);
}
}
static void
add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *reply = NULL;
gs_free_error GError *error = NULL;
reply = g_dbus_proxy_call_finish (proxy, result, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
priv->blobs_left--;
if (reply)
call_select_network (self);
else {
g_dbus_error_strip_remote_error (error);
_LOGW ("couldn't set network certificates: %s", error->message);
emit_error_helper (self, error);
}
}
static void
add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *reply = NULL;
gs_free_error GError *error = NULL;
GHashTable *blobs;
GHashTableIter iter;
const char *blob_name;
GByteArray *blob_data;
reply = _nm_dbus_proxy_call_finish (proxy, result,
G_VARIANT_TYPE ("(o)"),
&error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
g_free (priv->net_path);
priv->net_path = NULL;
if (error) {
g_dbus_error_strip_remote_error (error);
_LOGW ("adding network to supplicant failed: %s", error->message);
emit_error_helper (self, error);
return;
}
g_variant_get (reply, "(o)", &priv->net_path);
/* Send blobs first; otherwise jump to selecting the network */
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, (gpointer) &blob_name, (gpointer) &blob_data)) {
g_dbus_proxy_call (priv->iface_proxy,
"AddBlob",
g_variant_new ("(s@ay)",
blob_name,
g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
blob_data->data, blob_data->len, 1)),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->assoc_cancellable,
(GAsyncReadyCallback) add_blob_cb,
self);
}
call_select_network (self);
}
static void
add_network (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
g_dbus_proxy_call (priv->iface_proxy,
"AddNetwork",
g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->assoc_cancellable,
(GAsyncReadyCallback) add_network_cb,
self);
}
static void
set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *reply = NULL;
gs_free_error GError *error = NULL;
reply = g_dbus_proxy_call_finish (proxy, result, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
if (!reply) {
g_dbus_error_strip_remote_error (error);
_LOGW ("couldn't send AP scan mode to the supplicant interface: %s",
error->message);
emit_error_helper (self, error);
return;
}
_LOGI ("config: set interface ap_scan to %d",
nm_supplicant_config_get_ap_scan (priv->cfg));
add_network (self);
}
gboolean
nm_supplicant_interface_set_config (NMSupplicantInterface *self,
NMSupplicantConfig *cfg,
GError **error)
{
NMSupplicantInterfacePrivate *priv;
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 ( priv->fast_support == NM_SUPPLICANT_FEATURE_NO
&& nm_supplicant_config_fast_required (cfg)) {
g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
"EAP-FAST is not supported by the supplicant");
return FALSE;
}
g_clear_object (&priv->cfg);
if (cfg) {
priv->assoc_cancellable = g_cancellable_new ();
priv->cfg = g_object_ref (cfg);
g_dbus_proxy_call (priv->iface_proxy,
DBUS_INTERFACE_PROPERTIES ".Set",
g_variant_new ("(ssv)",
WPAS_DBUS_IFACE_INTERFACE,
"ApScan",
g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->cfg))),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->assoc_cancellable,
(GAsyncReadyCallback) set_ap_scan_cb,
self);
}
return TRUE;
}
static void
scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
gs_unref_variant GVariant *reply = NULL;
gs_free_error GError *error = NULL;
reply = g_dbus_proxy_call_finish (proxy, result, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = NM_SUPPLICANT_INTERFACE (user_data);
if (error) {
if (_nm_dbus_error_has_name (error, "fi.w1.wpa_supplicant1.Interface.ScanError"))
_LOGD ("could not get scan request result: %s", error->message);
else {
g_dbus_error_strip_remote_error (error);
_LOGW ("could not get scan request result: %s", error->message);
}
}
}
gboolean
nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArray *ssids)
{
NMSupplicantInterfacePrivate *priv;
GVariantBuilder builder;
guint i;
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
/* Scan parameters */
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&builder, "{sv}", "Type", g_variant_new_string ("active"));
if (ssids) {
GVariantBuilder ssids_builder;
g_variant_builder_init (&ssids_builder, G_VARIANT_TYPE_BYTESTRING_ARRAY);
for (i = 0; i < ssids->len; i++) {
GByteArray *ssid = g_ptr_array_index (ssids, i);
g_variant_builder_add (&ssids_builder, "@ay",
g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
ssid->data, ssid->len, 1));
}
g_variant_builder_add (&builder, "{sv}", "SSIDs", g_variant_builder_end (&ssids_builder));
}
g_dbus_proxy_call (priv->iface_proxy,
"Scan",
g_variant_new ("(a{sv})", &builder),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->other_cancellable,
(GAsyncReadyCallback) scan_request_cb,
self);
return TRUE;
}
NMSupplicantInterfaceState
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;
}
NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplicantInterfaceState,
NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID, "invalid"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INIT, "init"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_STARTING, "starting"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_READY, "ready"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISABLED, "disabled"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, "disconnected"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INACTIVE, "inactive"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_SCANNING, "scanning"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING, "authenticating"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING, "associating"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED, "associated"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE, "4-way handshake"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE, "group handshake"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_COMPLETED, "completed"),
NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DOWN, "down"),
);
const char *
nm_supplicant_interface_get_object_path (NMSupplicantInterface *self)
{
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 (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
}
guint
nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self)
{
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), 0);
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->max_scan_ssids;
}
/*****************************************************************************/
NMSupplicantInterface *
nm_supplicant_interface_new (const char *ifname,
NMSupplicantDriver driver,
NMSupplicantFeature fast_support,
NMSupplicantFeature ap_support)
{
g_return_val_if_fail (ifname != NULL, NULL);
return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
NM_SUPPLICANT_INTERFACE_IFACE, ifname,
NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver,
NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
NULL);
}
static void
nm_supplicant_interface_init (NMSupplicantInterface * self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
}
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
switch (prop_id) {
case PROP_IFACE:
/* construct-only */
priv->dev = g_value_dup_string (value);
g_return_if_fail (priv->dev);
break;
case PROP_DRIVER:
/* construct-only */
priv->driver = g_value_get_uint (value);
break;
case PROP_FAST_SUPPORT:
/* construct-only */
priv->fast_support = g_value_get_int (value);
break;
case PROP_AP_SUPPORT:
/* construct-only */
priv->ap_support = g_value_get_int (value);
break;
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)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
switch (prop_id) {
case PROP_SCANNING:
g_value_set_boolean (value, priv->scanning);
break;
case PROP_CURRENT_BSS:
g_value_set_string (value, priv->current_bss);
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 ((NMSupplicantInterface *) object);
if (priv->iface_proxy)
g_signal_handlers_disconnect_by_data (priv->iface_proxy, object);
g_clear_object (&priv->iface_proxy);
nm_clear_g_cancellable (&priv->init_cancellable);
nm_clear_g_cancellable (&priv->other_cancellable);
nm_clear_g_cancellable (&priv->assoc_cancellable);
g_clear_object (&priv->wpas_proxy);
g_clear_pointer (&priv->bss_proxies, (GDestroyNotify) g_hash_table_destroy);
g_clear_pointer (&priv->net_path, g_free);
g_clear_pointer (&priv->dev, g_free);
g_clear_pointer (&priv->object_path, g_free);
g_clear_pointer (&priv->current_bss, g_free);
g_clear_object (&priv->cfg);
/* 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);
object_class->dispose = dispose;
object_class->set_property = set_property;
object_class->get_property = get_property;
obj_properties[PROP_SCANNING] =
g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_SCANNING, "", "",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_CURRENT_BSS] =
g_param_spec_string (NM_SUPPLICANT_INTERFACE_CURRENT_BSS, "", "",
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_IFACE] =
g_param_spec_string (NM_SUPPLICANT_INTERFACE_IFACE, "", "",
NULL,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_DRIVER] =
g_param_spec_uint (NM_SUPPLICANT_INTERFACE_DRIVER, "", "",
0, G_MAXUINT, NM_SUPPLICANT_DRIVER_WIRELESS,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_FAST_SUPPORT] =
g_param_spec_int (NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, "", "",
NM_SUPPLICANT_FEATURE_UNKNOWN,
NM_SUPPLICANT_FEATURE_YES,
NM_SUPPLICANT_FEATURE_UNKNOWN,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_AP_SUPPORT] =
g_param_spec_int (NM_SUPPLICANT_INTERFACE_AP_SUPPORT, "", "",
NM_SUPPLICANT_FEATURE_UNKNOWN,
NM_SUPPLICANT_FEATURE_YES,
NM_SUPPLICANT_FEATURE_UNKNOWN,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
signals[STATE] =
g_signal_new (NM_SUPPLICANT_INTERFACE_STATE,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
signals[REMOVED] =
g_signal_new (NM_SUPPLICANT_INTERFACE_REMOVED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
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,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT);
signals[BSS_UPDATED] =
g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT);
signals[BSS_REMOVED] =
g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_REMOVED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals[SCAN_DONE] =
g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
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,
0,
NULL, NULL, NULL,
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,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
}