diff options
-rw-r--r-- | src/devices/nm-device-ethernet.c | 238 | ||||
-rw-r--r-- | src/devices/nm-device-macsec.c | 294 | ||||
-rw-r--r-- | src/devices/wifi/nm-device-iwd.c | 124 | ||||
-rw-r--r-- | src/devices/wifi/nm-device-wifi-p2p.c | 179 | ||||
-rw-r--r-- | src/devices/wifi/nm-device-wifi.c | 311 | ||||
-rw-r--r-- | src/devices/wifi/nm-wifi-ap.c | 295 | ||||
-rw-r--r-- | src/devices/wifi/nm-wifi-ap.h | 12 | ||||
-rw-r--r-- | src/devices/wifi/nm-wifi-p2p-peer.c | 108 | ||||
-rw-r--r-- | src/devices/wifi/nm-wifi-p2p-peer.h | 10 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-interface.c | 3781 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-interface.h | 49 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-manager.c | 1354 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-manager.h | 36 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-types.h | 63 |
14 files changed, 4069 insertions, 2785 deletions
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index c9dd75d6af..0f66fe8680 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -43,6 +43,8 @@ _LOG_DECLARE_SELF(NMDeviceEthernet); #define PPPOE_RECONNECT_DELAY 7 #define PPPOE_ENCAP_OVERHEAD 8 /* 2 bytes for PPP, 6 for PPPoE */ +#define SUPPLICANT_LNK_TIMEOUT_SEC 15 + /*****************************************************************************/ typedef enum { @@ -74,16 +76,17 @@ typedef struct _NMDeviceEthernetPrivate { struct { NMSupplicantManager *mgr; + NMSupplMgrCreateIfaceHandle *create_handle; NMSupplicantInterface *iface; - /* signal handler ids */ gulong iface_state_id; gulong auth_state_id; - /* Timeouts and idles */ guint con_timeout_id; - guint timeout_id; + guint lnk_timeout_id; + + bool is_associated:1; } supplicant; NMActRequestGetSecretsCallId *wired_secrets_id; @@ -399,7 +402,9 @@ supplicant_interface_release (NMDeviceEthernet *self) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - nm_clear_g_source (&priv->supplicant.timeout_id); + nm_clear_pointer (&priv->supplicant.create_handle, nm_supplicant_manager_create_interface_cancel); + + nm_clear_g_source (&priv->supplicant.lnk_timeout_id); nm_clear_g_source (&priv->supplicant.con_timeout_id); nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id); nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.auth_state_id); @@ -537,7 +542,7 @@ wired_secrets_get_secrets (NMDeviceEthernet *self, } static gboolean -link_timeout_cb (gpointer user_data) +supplicant_lnk_timeout_cb (gpointer user_data) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); @@ -546,13 +551,13 @@ link_timeout_cb (gpointer user_data) NMConnection *applied_connection; const char *setting_name; - priv->supplicant.timeout_id = 0; + priv->supplicant.lnk_timeout_id = 0; req = nm_device_get_act_request (device); if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { wired_auth_cond_fail (self, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); - return FALSE; + return G_SOURCE_REMOVE; } /* Disconnect event during initial authentication and credentials @@ -577,13 +582,13 @@ link_timeout_cb (gpointer user_data) nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); wired_secrets_get_secrets (self, setting_name, NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); - return FALSE; + return G_SOURCE_REMOVE; time_out: _LOGW (LOGD_DEVICE | LOGD_ETHER, "link timed out."); wired_auth_cond_fail (self, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - return FALSE; + return G_SOURCE_REMOVE; } static NMSupplicantConfig * @@ -616,18 +621,86 @@ build_supplicant_config (NMDeviceEthernet *self, } static void +supplicant_iface_state_is_completed (NMDeviceEthernet *self, + NMSupplicantInterfaceState state) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (state == NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { + nm_clear_g_source (&priv->supplicant.lnk_timeout_id); + nm_clear_g_source (&priv->supplicant.con_timeout_id); + + /* If this is the initial association during device activation, + * schedule the next activation stage. + */ + if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_CONFIG) { + _LOGI (LOGD_DEVICE | LOGD_ETHER, + "Activation: (ethernet) Stage 2 of 5 (Device Configure) successful."); + nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self)); + } + return; + } + + if ( !priv->supplicant.lnk_timeout_id + && !priv->supplicant.con_timeout_id) + priv->supplicant.lnk_timeout_id = g_timeout_add_seconds (SUPPLICANT_LNK_TIMEOUT_SEC, supplicant_lnk_timeout_cb, self); +} + +static void supplicant_iface_assoc_cb (NMSupplicantInterface *iface, GError *error, gpointer user_data) { - NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernet *self; + NMDeviceEthernetPrivate *priv; - if (error && !nm_utils_error_is_cancelled_or_disposing (error)) { + if (nm_utils_error_is_cancelled_or_disposing (error)) + return; + + self = NM_DEVICE_ETHERNET (user_data); + priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (error) { supplicant_interface_release (self); nm_device_queue_state (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + return; } + + nm_assert (!priv->supplicant.lnk_timeout_id); + nm_assert (!priv->supplicant.is_associated); + + priv->supplicant.is_associated = TRUE; + supplicant_iface_state_is_completed (self, + nm_supplicant_interface_get_state (priv->supplicant.iface)); +} + +static gboolean +supplicant_iface_start (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + gs_unref_object NMSupplicantConfig *config = NULL; + gs_free_error GError *error = NULL; + + config = build_supplicant_config (self, &error); + if (!config) { + _LOGE (LOGD_DEVICE | LOGD_ETHER, + "Activation: (ethernet) couldn't build security configuration: %s", + error->message); + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + return FALSE; + } + + nm_supplicant_interface_disconnect (priv->supplicant.iface); + nm_supplicant_interface_assoc (priv->supplicant.iface, + config, + supplicant_iface_assoc_cb, + self); + return TRUE; } static void @@ -639,69 +712,26 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - NMDevice *device = NM_DEVICE (self); - NMSupplicantConfig *config; - NMDeviceState devstate; - GError *error = NULL; NMSupplicantInterfaceState new_state = new_state_i; NMSupplicantInterfaceState old_state = old_state_i; - if (new_state == old_state) - return; - _LOGI (LOGD_DEVICE | LOGD_ETHER, "supplicant interface state: %s -> %s", nm_supplicant_interface_state_to_string (old_state), nm_supplicant_interface_state_to_string (new_state)); - devstate = nm_device_get_state (device); - - switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: - config = build_supplicant_config (self, &error); - if (config) { - nm_supplicant_interface_assoc (priv->supplicant.iface, config, - supplicant_iface_assoc_cb, self); - g_object_unref (config); - } else { - _LOGE (LOGD_DEVICE | LOGD_ETHER, - "Activation: (ethernet) couldn't build security configuration: %s", - error->message); - g_clear_error (&error); - - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: - nm_clear_g_source (&priv->supplicant.timeout_id); - nm_clear_g_source (&priv->supplicant.con_timeout_id); - - /* If this is the initial association during device activation, - * schedule the next activation stage. - */ - if (devstate == NM_DEVICE_STATE_CONFIG) { - _LOGI (LOGD_DEVICE | LOGD_ETHER, - "Activation: (ethernet) Stage 2 of 5 (Device Configure) successful."); - nm_device_activate_schedule_stage3_ip_config_start (device); - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED: - if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { - /* Start the link timeout so we allow some time for reauthentication */ - if (!priv->supplicant.timeout_id) - priv->supplicant.timeout_id = g_timeout_add_seconds (15, link_timeout_cb, device); - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { supplicant_interface_release (self); + wired_auth_cond_fail (self, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } - if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) - wired_auth_cond_fail (self, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - break; - default: - break; + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + if (!supplicant_iface_start (self)) + return; } + + if (priv->supplicant.is_associated) + supplicant_iface_state_is_completed (self, new_state); } static NMActStageReturn @@ -770,43 +800,70 @@ supplicant_connection_timeout_cb (gpointer user_data) if (nm_settings_connection_get_timestamp (connection, ×tamp)) new_secrets = !timestamp; - if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_FAILURE) + if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_FAILURE) { wired_auth_cond_fail (self, NM_DEVICE_STATE_REASON_NO_SECRETS); + return G_SOURCE_REMOVE; + } + + if ( !priv->supplicant.lnk_timeout_id + && priv->supplicant.iface) { + NMSupplicantInterfaceState state; - return FALSE; + state = nm_supplicant_interface_get_state (priv->supplicant.iface); + if (state != NM_SUPPLICANT_INTERFACE_STATE_COMPLETED + && NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (state)) + priv->supplicant.lnk_timeout_id = g_timeout_add_seconds (SUPPLICANT_LNK_TIMEOUT_SEC, supplicant_lnk_timeout_cb, self); + } + + return G_SOURCE_REMOVE; } -static gboolean -supplicant_interface_init (NMDeviceEthernet *self) +static void +supplicant_interface_create_cb (NMSupplicantManager *supplicant_manager, + NMSupplMgrCreateIfaceHandle *handle, + NMSupplicantInterface *iface, + GError *error, + gpointer user_data) { - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMDeviceEthernet *self; + NMDeviceEthernetPrivate *priv; guint timeout; - supplicant_interface_release (self); + if (nm_utils_error_is_cancelled (error)) + return; + + self = user_data; + priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - priv->supplicant.iface = nm_supplicant_manager_create_interface (priv->supplicant.mgr, - nm_device_get_iface (NM_DEVICE (self)), - NM_SUPPLICANT_DRIVER_WIRED); + nm_assert (priv->supplicant.create_handle == handle); + priv->supplicant.create_handle = NULL; - if (!priv->supplicant.iface) { + if (error) { _LOGE (LOGD_DEVICE | LOGD_ETHER, - "Couldn't initialize supplicant interface"); - return FALSE; + "Couldn't initialize supplicant interface: %s", + error->message); + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; } - /* Listen for its state signals */ + priv->supplicant.iface = g_object_ref (iface); + priv->supplicant.is_associated = FALSE; + priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface, NM_SUPPLICANT_INTERFACE_STATE, G_CALLBACK (supplicant_iface_state_cb), self); - /* Set up a timeout on the connection attempt */ timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self)); priv->supplicant.con_timeout_id = g_timeout_add_seconds (timeout, supplicant_connection_timeout_cb, self); - return TRUE; + if (NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (nm_supplicant_interface_get_state (iface))) + supplicant_iface_start (self); } static NMPlatformLinkDuplexType @@ -973,18 +1030,21 @@ supplicant_check_secrets_needed (NMDeviceEthernet *self, NMDeviceStateReason *ou ret = handle_auth_or_fail (self, req, FALSE); if (ret != NM_ACT_STAGE_RETURN_POSTPONE) NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - } else { - _LOGI (LOGD_DEVICE | LOGD_ETHER, - "Activation: (ethernet) connection '%s' requires no security. No secrets needed.", - nm_connection_get_id (connection)); - - if (supplicant_interface_init (self)) - ret = NM_ACT_STAGE_RETURN_POSTPONE; - else - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return ret; } - return ret; + _LOGI (LOGD_DEVICE | LOGD_ETHER, + "Activation: (ethernet) connection '%s' requires no security. No secrets needed.", + nm_connection_get_id (connection)); + + supplicant_interface_release (self); + + priv->supplicant.create_handle = nm_supplicant_manager_create_interface (priv->supplicant.mgr, + nm_device_get_ifindex (NM_DEVICE (self)), + NM_SUPPLICANT_DRIVER_WIRED, + supplicant_interface_create_cb, + self); + return NM_ACT_STAGE_RETURN_POSTPONE; } static void diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index fb18736703..bf8c6c7898 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -23,6 +23,10 @@ _LOG_DECLARE_SELF(NMDeviceMacsec); /*****************************************************************************/ +#define SUPPLICANT_LNK_TIMEOUT_SEC 15 + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceMacsec, PROP_SCI, PROP_CIPHER_SUITE, @@ -45,16 +49,17 @@ typedef struct { struct { NMSupplicantManager *mgr; + NMSupplMgrCreateIfaceHandle *create_handle; NMSupplicantInterface *iface; - /* signal handler ids */ gulong iface_state_id; - /* Timeouts and idles */ guint con_timeout_id; + guint lnk_timeout_id; + + bool is_associated:1; } supplicant; - guint supplicant_timeout_id; NMActRequestGetSecretsCallId *macsec_secrets_id; } NMDeviceMacsecPrivate; @@ -254,7 +259,9 @@ supplicant_interface_release (NMDeviceMacsec *self) { NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); - nm_clear_g_source (&priv->supplicant_timeout_id); + nm_clear_pointer (&priv->supplicant.create_handle, nm_supplicant_manager_create_interface_cancel); + + nm_clear_g_source (&priv->supplicant.lnk_timeout_id); nm_clear_g_source (&priv->supplicant.con_timeout_id); nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id); @@ -265,21 +272,6 @@ supplicant_interface_release (NMDeviceMacsec *self) } static void -supplicant_iface_assoc_cb (NMSupplicantInterface *iface, - GError *error, - gpointer user_data) -{ - NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data); - - if (error && !nm_utils_error_is_cancelled_or_disposing (error)) { - supplicant_interface_release (self); - nm_device_queue_state (NM_DEVICE (self), - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); - } -} - -static void macsec_secrets_cb (NMActRequest *req, NMActRequestGetSecretsCallId *call_id, NMSettingsConnection *connection, @@ -351,7 +343,7 @@ macsec_secrets_get_secrets (NMDeviceMacsec *self, } static gboolean -link_timeout_cb (gpointer user_data) +supplicant_lnk_timeout_cb (gpointer user_data) { NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data); NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); @@ -360,7 +352,7 @@ link_timeout_cb (gpointer user_data) NMConnection *applied_connection; const char *setting_name; - priv->supplicant_timeout_id = 0; + priv->supplicant.lnk_timeout_id = 0; req = nm_device_get_act_request (dev); @@ -368,7 +360,7 @@ link_timeout_cb (gpointer user_data) nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); - return FALSE; + return G_SOURCE_REMOVE; } /* Disconnect event during initial authentication and credentials @@ -392,13 +384,98 @@ link_timeout_cb (gpointer user_data) nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); macsec_secrets_get_secrets (self, setting_name, NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); - return FALSE; + return G_SOURCE_REMOVE; time_out: _LOGW (LOGD_DEVICE | LOGD_ETHER, "link timed out."); nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - return FALSE; + return G_SOURCE_REMOVE; +} + +static void +supplicant_iface_state_is_completed (NMDeviceMacsec *self, + NMSupplicantInterfaceState state) +{ + NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); + + if (state == NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { + nm_clear_g_source (&priv->supplicant.lnk_timeout_id); + nm_clear_g_source (&priv->supplicant.con_timeout_id); + + nm_device_bring_up (NM_DEVICE (self), TRUE, NULL); + + /* If this is the initial association during device activation, + * schedule the next activation stage. + */ + if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_CONFIG) { + _LOGI (LOGD_DEVICE, + "Activation: Stage 2 of 5 (Device Configure) successful."); + nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self)); + } + return; + } + + if ( !priv->supplicant.lnk_timeout_id + && !priv->supplicant.con_timeout_id) + priv->supplicant.lnk_timeout_id = g_timeout_add_seconds (SUPPLICANT_LNK_TIMEOUT_SEC, supplicant_lnk_timeout_cb, self); +} + +static void +supplicant_iface_assoc_cb (NMSupplicantInterface *iface, + GError *error, + gpointer user_data) +{ + NMDeviceMacsec *self; + NMDeviceMacsecPrivate *priv; + + if (nm_utils_error_is_cancelled_or_disposing (error)) + return; + + self = user_data; + priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); + + if (error) { + supplicant_interface_release (self); + nm_device_queue_state (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + return; + } + + nm_assert (!priv->supplicant.lnk_timeout_id); + nm_assert (!priv->supplicant.is_associated); + + priv->supplicant.is_associated = TRUE; + supplicant_iface_state_is_completed (self, + nm_supplicant_interface_get_state (priv->supplicant.iface)); +} + +static gboolean +supplicant_iface_start (NMDeviceMacsec *self) +{ + NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); + gs_unref_object NMSupplicantConfig *config = NULL; + gs_free_error GError *error = NULL; + + config = build_supplicant_config (self, &error); + if (!config) { + _LOGE (LOGD_DEVICE, + "Activation: couldn't build security configuration: %s", + error->message); + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + return FALSE; + } + + nm_supplicant_interface_disconnect (priv->supplicant.iface); + nm_supplicant_interface_assoc (priv->supplicant.iface, + config, + supplicant_iface_assoc_cb, + self); + return TRUE; } static void @@ -410,73 +487,28 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, { NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data); NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); - NMDevice *device = NM_DEVICE (self); - NMSupplicantConfig *config; - NMDeviceState devstate; - GError *error = NULL; NMSupplicantInterfaceState new_state = new_state_i; NMSupplicantInterfaceState old_state = old_state_i; - if (new_state == old_state) - return; - _LOGI (LOGD_DEVICE, "supplicant interface state: %s -> %s", nm_supplicant_interface_state_to_string (old_state), nm_supplicant_interface_state_to_string (new_state)); - devstate = nm_device_get_state (device); - - switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: - config = build_supplicant_config (self, &error); - if (config) { - nm_supplicant_interface_assoc (priv->supplicant.iface, config, - supplicant_iface_assoc_cb, self); - g_object_unref (config); - } else { - _LOGE (LOGD_DEVICE, - "Activation: couldn't build security configuration: %s", - error->message); - g_clear_error (&error); - - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: - nm_clear_g_source (&priv->supplicant_timeout_id); - nm_clear_g_source (&priv->supplicant.con_timeout_id); - nm_device_bring_up (device, TRUE, NULL); - - /* If this is the initial association during device activation, - * schedule the next activation stage. - */ - if (devstate == NM_DEVICE_STATE_CONFIG) { - _LOGI (LOGD_DEVICE, - "Activation: Stage 2 of 5 (Device Configure) successful."); - nm_device_activate_schedule_stage3_ip_config_start (device); - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED: - if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { - /* Start the link timeout so we allow some time for reauthentication */ - if (!priv->supplicant_timeout_id) - priv->supplicant_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, device); - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } - if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - } - break; - default: - ; + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + if (!supplicant_iface_start (self)) + return; } + + if (priv->supplicant.is_associated) + supplicant_iface_state_is_completed (self, new_state); } static NMActStageReturn @@ -527,11 +559,10 @@ supplicant_connection_timeout_cb (gpointer user_data) "Activation: (macsec) association took too long."); supplicant_interface_release (self); - req = nm_device_get_act_request (device); - g_assert (req); + req = nm_device_get_act_request (device); connection = nm_act_request_get_settings_connection (req); - g_assert (connection); + g_return_val_if_fail (connection, G_SOURCE_REMOVE); /* Ask for new secrets only if we've never activated this connection * before. If we've connected before, don't bother the user with dialogs, @@ -540,48 +571,73 @@ supplicant_connection_timeout_cb (gpointer user_data) if (nm_settings_connection_get_timestamp (connection, ×tamp)) new_secrets = !timestamp; - if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE) - _LOGW (LOGD_DEVICE, "Activation: (macsec) asking for new secrets"); - else + if (handle_auth_or_fail (self, req, new_secrets) != NM_ACT_STAGE_RETURN_POSTPONE) { nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + return G_SOURCE_REMOVE; + } - return FALSE; + _LOGW (LOGD_DEVICE, "Activation: (macsec) asking for new secrets"); + + if ( !priv->supplicant.lnk_timeout_id + && priv->supplicant.iface) { + NMSupplicantInterfaceState state; + + state = nm_supplicant_interface_get_state (priv->supplicant.iface); + if (state != NM_SUPPLICANT_INTERFACE_STATE_COMPLETED + && NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (state)) + priv->supplicant.lnk_timeout_id = g_timeout_add_seconds (SUPPLICANT_LNK_TIMEOUT_SEC, supplicant_lnk_timeout_cb, self); + } + + return G_SOURCE_REMOVE; } -static gboolean -supplicant_interface_init (NMDeviceMacsec *self) +static void +supplicant_interface_create_cb (NMSupplicantManager *supplicant_manager, + NMSupplMgrCreateIfaceHandle *handle, + NMSupplicantInterface *iface, + GError *error, + gpointer user_data) { - NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); - NMDevice *parent; + NMDeviceMacsec *self; + NMDeviceMacsecPrivate *priv; guint timeout; - parent = nm_device_parent_get_device (NM_DEVICE (self)); - g_return_val_if_fail (parent, FALSE); + if (nm_utils_error_is_cancelled (error)) + return; - supplicant_interface_release (self); + self = user_data; + priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); + + nm_assert (priv->supplicant.create_handle == handle); - priv->supplicant.iface = nm_supplicant_manager_create_interface (priv->supplicant.mgr, - nm_device_get_iface (parent), - NM_SUPPLICANT_DRIVER_MACSEC); + priv->supplicant.create_handle = NULL; - if (!priv->supplicant.iface) { + if (error) { _LOGE (LOGD_DEVICE, - "Couldn't initialize supplicant interface"); - return FALSE; + "Couldn't initialize supplicant interface: %s", + error->message); + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; } - /* Listen for its state signals */ + priv->supplicant.iface = g_object_ref (iface); + priv->supplicant.is_associated = FALSE; + priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface, NM_SUPPLICANT_INTERFACE_STATE, G_CALLBACK (supplicant_iface_state_cb), self); - /* Set up a timeout on the connection attempt */ timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self)); priv->supplicant.con_timeout_id = g_timeout_add_seconds (timeout, supplicant_connection_timeout_cb, self); - return TRUE; + + if (NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (nm_supplicant_interface_get_state (iface))) + supplicant_iface_start (self); } static NMActStageReturn @@ -591,7 +647,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); NMConnection *connection; NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMDevice *parent; const char *setting_name; + int ifindex; connection = nm_device_get_applied_connection (NM_DEVICE (self)); @@ -612,18 +670,26 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) ret = handle_auth_or_fail (self, req, FALSE); if (ret != NM_ACT_STAGE_RETURN_POSTPONE) NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - } else { - _LOGI (LOGD_DEVICE | LOGD_ETHER, - "Activation: connection '%s' requires no security. No secrets needed.", - nm_connection_get_id (connection)); - - if (supplicant_interface_init (self)) - ret = NM_ACT_STAGE_RETURN_POSTPONE; - else - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return ret; } - return ret; + _LOGI (LOGD_DEVICE | LOGD_ETHER, + "Activation: connection '%s' requires no security. No secrets needed.", + nm_connection_get_id (connection)); + + supplicant_interface_release (self); + + parent = nm_device_parent_get_device (NM_DEVICE (self)); + g_return_val_if_fail (parent, NM_ACT_STAGE_RETURN_FAILURE); + ifindex = nm_device_get_ifindex (parent); + g_return_val_if_fail (ifindex > 0, NM_ACT_STAGE_RETURN_FAILURE); + + priv->supplicant.create_handle = nm_supplicant_manager_create_interface (priv->supplicant.mgr, + ifindex, + NM_SUPPLICANT_DRIVER_MACSEC, + supplicant_interface_create_cb, + self); + return NM_ACT_STAGE_RETURN_POSTPONE; } static void diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index b504c9bc90..0cf7f5125d 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -7,24 +7,26 @@ #include "nm-device-iwd.h" -#include "nm-libnm-core-intern/nm-common-macros.h" -#include "devices/nm-device.h" #include "devices/nm-device-private.h" -#include "nm-utils.h" +#include "devices/nm-device.h" #include "nm-act-request.h" +#include "nm-config.h" +#include "nm-core-internal.h" +#include "nm-dbus-manager.h" +#include "nm-glib-aux/nm-ref-string.h" +#include "nm-iwd-manager.h" +#include "nm-libnm-core-intern/nm-common-macros.h" +#include "nm-setting-8021x.h" #include "nm-setting-connection.h" -#include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" -#include "nm-setting-8021x.h" +#include "nm-setting-wireless.h" +#include "nm-std-aux/nm-dbus-compat.h" +#include "nm-utils.h" +#include "nm-wifi-common.h" +#include "nm-wifi-utils.h" #include "settings/nm-settings-connection.h" #include "settings/nm-settings.h" -#include "nm-wifi-utils.h" -#include "nm-wifi-common.h" -#include "nm-core-internal.h" -#include "nm-config.h" -#include "nm-iwd-manager.h" -#include "nm-dbus-manager.h" -#include "nm-std-aux/nm-dbus-compat.h" +#include "supplicant/nm-supplicant-types.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceIwd); @@ -191,45 +193,41 @@ remove_all_aps (NMDeviceIwd *self) nm_device_recheck_available_connections (NM_DEVICE (self)); } -static GVariant * -vardict_from_network_type (const char *type) +static NM80211ApSecurityFlags +ap_security_flags_from_network_type (const char *type) { - GVariantBuilder builder; - const char *key_mgmt = ""; - const char *pairwise = "ccmp"; + NM80211ApSecurityFlags flags; - if (!strcmp (type, "psk")) - key_mgmt = "wpa-psk"; - else if (!strcmp (type, "8021x")) - key_mgmt = "wpa-eap"; + if (nm_streq (type, "psk")) + flags = NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (nm_streq (type, "8021x")) + flags = NM_802_11_AP_SEC_KEY_MGMT_802_1X; else - return NULL; + return NM_802_11_AP_SEC_NONE; - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&builder, "{sv}", "KeyMgmt", - g_variant_new_strv (&key_mgmt, 1)); - g_variant_builder_add (&builder, "{sv}", "Pairwise", - g_variant_new_strv (&pairwise, 1)); - g_variant_builder_add (&builder, "{sv}", "Group", - g_variant_new_string ("ccmp")); - return g_variant_new ("a{sv}", &builder); + flags |= NM_802_11_AP_SEC_PAIR_CCMP; + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + return flags; } static void insert_ap_from_network (NMDeviceIwd *self, GHashTable *aps, const char *path, + gint64 last_seen_msec, int16_t signal, uint32_t ap_id) { gs_unref_object GDBusProxy *network_proxy = NULL; - gs_unref_variant GVariant *name_value = NULL, *type_value = NULL; - const char *name, *type; - GVariantBuilder builder; - gs_unref_variant GVariant *props = NULL; - GVariant *rsn; + gs_unref_variant GVariant *name_value = NULL; + gs_unref_variant GVariant *type_value = NULL; + nm_auto_ref_string NMRefString *bss_path = NULL; + const char *name; + const char *type; + NMSupplicantBssInfo bss_info; uint8_t bssid[6]; NMWifiAP *ap; + gs_unref_bytes GBytes *ssid = NULL; if (g_hash_table_lookup (aps, path)) { _LOGD (LOGD_WIFI, "Duplicate network at %s", path); @@ -253,6 +251,11 @@ insert_ap_from_network (NMDeviceIwd *self, name = g_variant_get_string (name_value, NULL); type = g_variant_get_string (type_value, NULL); + if (nm_streq (type, "wep")) { + /* WEP not supported */ + return; + } + /* What we get from IWD are networks, or ESSs, that may contain * multiple APs, or BSSs, each. We don't get information about any * specific BSSs within an ESS but we can safely present each ESS @@ -268,31 +271,24 @@ insert_ap_from_network (NMDeviceIwd *self, bssid[4] = ap_id >> 8; bssid[5] = ap_id; - /* WEP not supported */ - if (nm_streq (type, "wep")) - return; - - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&builder, "{sv}", "BSSID", - g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1)); - g_variant_builder_add (&builder, "{sv}", "Mode", - g_variant_new_string ("infrastructure")); - - rsn = vardict_from_network_type (type); - if (rsn) - g_variant_builder_add (&builder, "{sv}", "RSN", rsn); - - props = g_variant_new ("a{sv}", &builder); + ssid = g_bytes_new (name, NM_MIN (32u, strlen (name))); + bss_path = nm_ref_string_new (path); - ap = nm_wifi_ap_new_from_properties (path, props); + bss_info = (NMSupplicantBssInfo) { + .bss_path = bss_path, + .last_seen_msec = last_seen_msec, + .bssid_valid = TRUE, + .mode = NM_802_11_MODE_INFRA, + .rsn_flags = ap_security_flags_from_network_type (type), + .ssid = ssid, + .signal_percent = nm_wifi_utils_level_to_quality (signal / 100), + .frequency = 2417, + .max_rate = 65000, + }; + memcpy (bss_info.bssid, bssid, sizeof (bssid)); - nm_wifi_ap_set_ssid_arr (ap, - (const guint8 *) name, - NM_MIN (32, strlen (name))); + ap = nm_wifi_ap_new_from_properties (&bss_info); - nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100)); - nm_wifi_ap_set_freq (ap, 2417); - nm_wifi_ap_set_max_bitrate (ap, 65000); g_hash_table_insert (aps, (gpointer) nm_wifi_ap_get_supplicant_path (ap), ap); } @@ -313,6 +309,7 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) gboolean compat; const char *return_sig; static uint32_t ap_id = 0; + gint64 last_seen_msec; variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); if (!variant) { @@ -340,12 +337,13 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) g_variant_get (variant, return_sig, &networks); + last_seen_msec = nm_utils_get_monotonic_timestamp_msec (); if (compat) { while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) - insert_ap_from_network (self, new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, last_seen_msec, signal, ap_id++); } else { while (g_variant_iter_next (networks, "(&on)", &path, &signal)) - insert_ap_from_network (self, new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, last_seen_msec, signal, ap_id++); } g_variant_iter_free (networks); @@ -1148,7 +1146,7 @@ try_reply_agent_request (NMDeviceIwd *self, *replied = FALSE; - if (!strcmp (method_name, "RequestPassphrase")) { + if (nm_streq (method_name, "RequestPassphrase")) { const char *psk; if (!s_wireless_sec) @@ -1168,7 +1166,7 @@ try_reply_agent_request (NMDeviceIwd *self, *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; *setting_key = NM_SETTING_WIRELESS_SECURITY_PSK; return TRUE; - } else if (!strcmp (method_name, "RequestPrivateKeyPassphrase")) { + } else if (nm_streq (method_name, "RequestPrivateKeyPassphrase")) { const char *password; if (!s_8021x) @@ -1188,7 +1186,7 @@ try_reply_agent_request (NMDeviceIwd *self, *setting_name = NM_SETTING_802_1X_SETTING_NAME; *setting_key = NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD; return TRUE; - } else if (!strcmp (method_name, "RequestUserNameAndPassword")) { + } else if (nm_streq (method_name, "RequestUserNameAndPassword")) { const char *identity, *password; if (!s_8021x) @@ -1212,7 +1210,7 @@ try_reply_agent_request (NMDeviceIwd *self, else *setting_key = NM_SETTING_802_1X_PASSWORD; return TRUE; - } else if (!strcmp (method_name, "RequestUserPassword")) { + } else if (nm_streq (method_name, "RequestUserPassword")) { const char *password; if (!s_8021x) diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c index e3860ebf2f..08c904dd4c 100644 --- a/src/devices/wifi/nm-device-wifi-p2p.c +++ b/src/devices/wifi/nm-device-wifi-p2p.c @@ -12,19 +12,20 @@ #include "supplicant/nm-supplicant-manager.h" #include "supplicant/nm-supplicant-interface.h" -#include "nm-manager.h" -#include "nm-utils.h" -#include "nm-wifi-p2p-peer.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" -#include "settings/nm-settings.h" -#include "nm-setting-wifi-p2p.h" #include "nm-act-request.h" +#include "nm-core-internal.h" +#include "nm-glib-aux/nm-ref-string.h" #include "nm-ip4-config.h" -#include "platform/nm-platform.h" #include "nm-manager.h" -#include "nm-core-internal.h" +#include "nm-manager.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-utils.h" +#include "nm-wifi-p2p-peer.h" +#include "platform/nm-platform.h" #include "platform/nmp-object.h" +#include "settings/nm-settings.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceWifiP2P); @@ -227,11 +228,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) return FALSE; supplicant_state = nm_supplicant_interface_get_state (priv->mgmt_iface); - if ( supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY - || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) - return FALSE; - - return TRUE; + return NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (supplicant_state); } static gboolean @@ -649,52 +646,49 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, NMSupplicantInterfaceState new_state = new_state_i; NMSupplicantInterfaceState old_state = old_state_i; - if (new_state == old_state) - return; - _LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant management interface state: %s -> %s", nm_supplicant_interface_state_to_string (old_state), nm_supplicant_interface_state_to_string (new_state)); - switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: - _LOGD (LOGD_WIFI, "supplicant ready"); + if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + supplicant_interfaces_release (self, TRUE); nm_device_queue_recheck_available (device, NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } - if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) - _set_is_waiting_for_supplicant (self, FALSE); - break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: - supplicant_interfaces_release (self, TRUE); + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + _LOGD (LOGD_WIFI, "supplicant ready"); nm_device_queue_recheck_available (device, NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - break; - default: - break; + _set_is_waiting_for_supplicant (self, FALSE); } } static void -supplicant_iface_peer_updated_cb (NMSupplicantInterface *iface, - const char *object_path, - GVariant *properties, +supplicant_iface_peer_changed_cb (NMSupplicantInterface *iface, + NMSupplicantPeerInfo *peer_info, + gboolean is_present, NMDeviceWifiP2P *self) { - NMDeviceWifiP2PPrivate *priv; + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); NMWifiP2PPeer *found_peer; - g_return_if_fail (self != NULL); - g_return_if_fail (object_path != NULL); + found_peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, peer_info->peer_path->str); - priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + if (!is_present) { + if (!found_peer) + return; + + peer_add_remove (self, FALSE, found_peer, TRUE); + goto out; + } - found_peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path); if (found_peer) { - if (!nm_wifi_p2p_peer_update_from_properties (found_peer, object_path, properties)) + if (!nm_wifi_p2p_peer_update_from_properties (found_peer, peer_info)) return; update_disconnect_on_connection_peer_missing (self); @@ -702,35 +696,11 @@ supplicant_iface_peer_updated_cb (NMSupplicantInterface *iface, } else { gs_unref_object NMWifiP2PPeer *peer = NULL; - peer = nm_wifi_p2p_peer_new_from_properties (object_path, properties); - if (!peer) { - _LOGD (LOGD_WIFI, "invalid P2P peer properties received for %s", object_path); - return; - } - + peer = nm_wifi_p2p_peer_new_from_properties (peer_info); peer_add_remove (self, TRUE, peer, TRUE); } - schedule_peer_list_dump (self); -} - -static void -supplicant_iface_peer_removed_cb (NMSupplicantInterface *iface, - const char *object_path, - NMDeviceWifiP2P *self) -{ - NMDeviceWifiP2PPrivate *priv; - NMWifiP2PPeer *peer; - - g_return_if_fail (self != NULL); - g_return_if_fail (object_path != NULL); - - priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); - peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path); - if (!peer) - return; - - peer_add_remove (self, FALSE, peer, TRUE); +out: schedule_peer_list_dump (self); } @@ -742,7 +712,7 @@ check_group_iface_ready (NMDeviceWifiP2P *self) if (!priv->group_iface) return; - if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + if (!NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (nm_supplicant_interface_get_state (priv->group_iface))) return; if (!nm_supplicant_interface_get_p2p_group_joined (priv->group_iface)) @@ -755,6 +725,24 @@ check_group_iface_ready (NMDeviceWifiP2P *self) } static void +supplicant_group_iface_is_ready (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + _LOGD (LOGD_WIFI, "P2P Group supplicant ready"); + + if (!nm_device_set_ip_iface (NM_DEVICE (self), nm_supplicant_interface_get_ifname (priv->group_iface))) { + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } + + _set_is_waiting_for_supplicant (self, FALSE); + check_group_iface_ready (self); +} + +static void supplicant_group_iface_state_cb (NMSupplicantInterface *iface, int new_state_i, int old_state_i, @@ -762,44 +750,26 @@ supplicant_group_iface_state_cb (NMSupplicantInterface *iface, gpointer user_data) { NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); - NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); - NMDevice *device = NM_DEVICE (self); NMSupplicantInterfaceState new_state = new_state_i; NMSupplicantInterfaceState old_state = old_state_i; - if (new_state == old_state) - return; - _LOGI (LOGD_DEVICE | LOGD_WIFI, "P2P Group supplicant interface state: %s -> %s", nm_supplicant_interface_state_to_string (old_state), nm_supplicant_interface_state_to_string (new_state)); - switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: - _LOGD (LOGD_WIFI, "P2P Group supplicant ready"); - - if (!nm_device_set_ip_iface (device, nm_supplicant_interface_get_ifname (priv->group_iface))) { - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - break; - } - - if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) - _set_is_waiting_for_supplicant (self, FALSE); - - check_group_iface_ready (self); - break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { supplicant_group_interface_release (self); - nm_device_state_changed (device, + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - break; - default: - break; + return; + } + + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + supplicant_group_iface_is_ready (self); + return; } } @@ -833,8 +803,9 @@ supplicant_iface_group_started_cb (NMSupplicantInterface *iface, NMDeviceWifiP2P *self) { NMDeviceWifiP2PPrivate *priv; + NMSupplicantInterfaceState state; - g_return_if_fail (self != NULL); + g_return_if_fail (self); if (!nm_device_is_activating (NM_DEVICE (self))) { _LOGW (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant notified a group start but we are not trying to connect! Ignoring the event."); @@ -844,6 +815,7 @@ supplicant_iface_group_started_cb (NMSupplicantInterface *iface, priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); supplicant_group_interface_release (self); + priv->group_iface = g_object_ref (group_iface); /* We need to wait for the interface to be ready and the group @@ -862,10 +834,13 @@ supplicant_iface_group_started_cb (NMSupplicantInterface *iface, G_CALLBACK (supplicant_group_iface_group_finished_cb), self); - if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + state = nm_supplicant_interface_get_state (priv->group_iface); + if (state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) { _set_is_waiting_for_supplicant (self, TRUE); + return; + } - check_group_iface_ready (self); + supplicant_group_iface_is_ready (self); } static void @@ -935,9 +910,8 @@ device_state_changed (NMDevice *device, break; case NM_DEVICE_STATE_UNAVAILABLE: if ( !priv->mgmt_iface - || nm_supplicant_interface_get_state (priv->mgmt_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + || !NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (nm_supplicant_interface_get_state (priv->mgmt_iface))) _set_is_waiting_for_supplicant (self, TRUE); - break; case NM_DEVICE_STATE_NEED_AUTH: /* Disconnect? */ @@ -1084,20 +1058,20 @@ nm_device_wifi_p2p_set_mgmt_iface (NMDeviceWifiP2P *self, goto done; _LOGD (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant management interface changed to %s.", - nm_supplicant_interface_get_object_path (iface)); + nm_ref_string_get_str (nm_supplicant_interface_get_object_path (iface))); priv->mgmt_iface = g_object_ref (iface); - g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_STATE, + g_signal_connect (priv->mgmt_iface, + NM_SUPPLICANT_INTERFACE_STATE, G_CALLBACK (supplicant_iface_state_cb), self); - g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_UPDATED, - G_CALLBACK (supplicant_iface_peer_updated_cb), - self); - g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_REMOVED, - G_CALLBACK (supplicant_iface_peer_removed_cb), + g_signal_connect (priv->mgmt_iface, + NM_SUPPLICANT_INTERFACE_PEER_CHANGED, + G_CALLBACK (supplicant_iface_peer_changed_cb), self); - g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + g_signal_connect (priv->mgmt_iface, + NM_SUPPLICANT_INTERFACE_GROUP_STARTED, G_CALLBACK (supplicant_iface_group_started_cb), self); done: @@ -1106,8 +1080,7 @@ done: NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); _set_is_waiting_for_supplicant (self, !priv->mgmt_iface - || ( nm_supplicant_interface_get_state (priv->mgmt_iface) - < NM_SUPPLICANT_INTERFACE_STATE_READY)); + || !NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (nm_supplicant_interface_get_state (priv->mgmt_iface))); } void diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 6df55e3a46..1beeb4becf 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -11,6 +11,7 @@ #include <netinet/in.h> #include <unistd.h> +#include "nm-glib-aux/nm-ref-string.h" #include "nm-device-wifi-p2p.h" #include "nm-wifi-ap.h" #include "nm-libnm-core-intern/nm-common-macros.h" @@ -90,6 +91,7 @@ typedef struct { guint ap_dump_id; NMSupplicantManager *sup_mgr; + NMSupplMgrCreateIfaceHandle *sup_create_handle; NMSupplicantInterface *sup_iface; guint sup_timeout_id; /* supplicant association timeout */ @@ -137,28 +139,31 @@ G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) static gboolean check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic); +static void supplicant_iface_state_down (NMDeviceWifi *self); + static void schedule_scan (NMDeviceWifi *self, gboolean backoff); static void cleanup_association_attempt (NMDeviceWifi * self, gboolean disconnect); +static void supplicant_iface_state (NMDeviceWifi *self, + NMSupplicantInterfaceState new_state, + NMSupplicantInterfaceState old_state, + int disconnect_reason, + gboolean is_real_signal); + static void supplicant_iface_state_cb (NMSupplicantInterface *iface, int new_state_i, int old_state_i, int disconnect_reason, gpointer user_data); -static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, - const char *object_path, - GVariant *properties, - NMDeviceWifi *self); - -static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, - const char *object_path, +static void supplicant_iface_bss_changed_cb (NMSupplicantInterface *iface, + NMSupplicantBssInfo *bss_info, + gboolean is_present, NMDeviceWifi *self); static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, - gboolean success, NMDeviceWifi * self); static void supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, @@ -237,36 +242,40 @@ unmanaged_on_quit (NMDevice *self) return TRUE; } -static gboolean -supplicant_interface_acquire (NMDeviceWifi *self) +static void +supplicant_interface_acquire_cb (NMSupplicantManager *supplicant_manager, + NMSupplMgrCreateIfaceHandle *handle, + NMSupplicantInterface *iface, + GError *error, + gpointer user_data) { + NMDeviceWifi *self = user_data; NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (!priv->sup_iface, TRUE); + if (nm_utils_error_is_cancelled (error)) + return; - priv->sup_iface = nm_supplicant_manager_create_interface (priv->sup_mgr, - nm_device_get_iface (NM_DEVICE (self)), - NM_SUPPLICANT_DRIVER_WIRELESS); - if (!priv->sup_iface) { - _LOGE (LOGD_WIFI, "Couldn't initialize supplicant interface"); - return FALSE; + nm_assert (priv->sup_create_handle == handle); + + priv->sup_create_handle = NULL; + + if (error) { + _LOGE (LOGD_WIFI, "Couldn't initialize supplicant interface: %s", + error->message); + supplicant_iface_state_down (self); + nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); + return; } - if (nm_supplicant_interface_get_state (priv->sup_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) - nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, FALSE); + priv->sup_iface = g_object_ref (iface); g_signal_connect (priv->sup_iface, NM_SUPPLICANT_INTERFACE_STATE, G_CALLBACK (supplicant_iface_state_cb), self); g_signal_connect (priv->sup_iface, - NM_SUPPLICANT_INTERFACE_BSS_UPDATED, - G_CALLBACK (supplicant_iface_bss_updated_cb), - self); - g_signal_connect (priv->sup_iface, - NM_SUPPLICANT_INTERFACE_BSS_REMOVED, - G_CALLBACK (supplicant_iface_bss_removed_cb), + NM_SUPPLICANT_INTERFACE_BSS_CHANGED, + G_CALLBACK (supplicant_iface_bss_changed_cb), self); g_signal_connect (priv->sup_iface, NM_SUPPLICANT_INTERFACE_SCAN_DONE, @@ -291,7 +300,30 @@ supplicant_interface_acquire (NMDeviceWifi *self) _notify_scanning (self); - return TRUE; + if (nm_supplicant_interface_get_state (priv->sup_iface) != NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + /* fake an initial state change. */ + supplicant_iface_state (user_data, + NM_SUPPLICANT_INTERFACE_STATE_STARTING, + nm_supplicant_interface_get_state (priv->sup_iface), + 0, + FALSE); + } +} + +static void +supplicant_interface_acquire (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_assert (!priv->sup_iface); + nm_assert (!priv->sup_create_handle); + + priv->sup_create_handle = nm_supplicant_manager_create_interface (priv->sup_mgr, + nm_device_get_ifindex (NM_DEVICE (self)), + NM_SUPPLICANT_DRIVER_WIRELESS, + supplicant_interface_acquire_cb, + self); + nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); } static void @@ -310,18 +342,17 @@ _requested_scan_set (NMDeviceWifi *self, gboolean value) nm_device_add_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE); else { nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); - nm_device_remove_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE); + nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WIFI_SCAN, TRUE); } } static void supplicant_interface_release (NMDeviceWifi *self) { - NMDeviceWifiPrivate *priv; - - g_return_if_fail (self != NULL); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + if (nm_clear_pointer (&priv->sup_create_handle, nm_supplicant_manager_create_interface_cancel)) + nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); _requested_scan_set (self, FALSE); @@ -936,7 +967,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) return FALSE; supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface); - if ( supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY + if ( supplicant_state <= NM_SUPPLICANT_INTERFACE_STATE_STARTING || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) return FALSE; @@ -1198,7 +1229,8 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, } last_scan = nm_supplicant_interface_get_last_scan (priv->sup_iface); - if (last_scan && (nm_utils_get_monotonic_timestamp_msec () - last_scan) < 10 * NM_UTILS_MSEC_PER_SEC) { + if ( last_scan > 0 + && (nm_utils_get_monotonic_timestamp_msec () - last_scan) < 10 * NM_UTILS_MSEC_PER_SEC) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1469,16 +1501,15 @@ schedule_scan (NMDeviceWifi *self, gboolean backoff) static void supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, - gboolean success, NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - _LOGD (LOGD_WIFI, "wifi-scan: scan-done callback: %s", success ? "successful" : "failed"); + _LOGD (LOGD_WIFI, "wifi-scan: scan-done callback"); priv->last_scan = nm_utils_get_monotonic_timestamp_msec (); _notify (self, PROP_LAST_SCAN); - schedule_scan (self, success); + schedule_scan (self, TRUE); _requested_scan_set (self, FALSE); } @@ -1552,40 +1583,43 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self, } static void -supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, - const char *object_path, - GVariant *properties, +supplicant_iface_bss_changed_cb (NMSupplicantInterface *iface, + NMSupplicantBssInfo *bss_info, + gboolean is_present, NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMDeviceState state; - NMWifiAP *found_ap = NULL; + NMWifiAP *found_ap; GBytes *ssid; - g_return_if_fail (self != NULL); - g_return_if_fail (properties != NULL); - g_return_if_fail (iface != NULL); + found_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, bss_info->bss_path->str); - /* Ignore new APs when unavailable, unmanaged, or in AP mode */ - state = nm_device_get_state (NM_DEVICE (self)); - if (state <= NM_DEVICE_STATE_UNAVAILABLE) - return; - if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) + if (!is_present) { + if (!found_ap) + return; + if (found_ap == priv->current_ap) { + /* The current AP cannot be removed (to prevent NM indicating that + * it is connected, but to nothing), but it must be removed later + * when the current AP is changed or cleared. Set 'fake' to + * indicate that this AP is now unknown to the supplicant. + */ + if (nm_wifi_ap_set_fake (found_ap, TRUE)) + _ap_dump (self, LOGL_DEBUG, found_ap, "updated", 0); + } else { + ap_add_remove (self, FALSE, found_ap, TRUE); + schedule_ap_list_dump (self); + } return; + } - found_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path); if (found_ap) { - if (!nm_wifi_ap_update_from_properties (found_ap, object_path, properties)) + if (!nm_wifi_ap_update_from_properties (found_ap, bss_info)) return; _ap_dump (self, LOGL_DEBUG, found_ap, "updated", 0); } else { gs_unref_object NMWifiAP *ap = NULL; - ap = nm_wifi_ap_new_from_properties (object_path, properties); - if (!ap) { - _LOGD (LOGD_WIFI, "invalid AP properties received for %s", object_path); - return; - } + ap = nm_wifi_ap_new_from_properties (bss_info); /* Let the manager try to fill in the SSID from seen-bssids lists */ ssid = nm_wifi_ap_get_ssid (ap); @@ -1615,43 +1649,13 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, /* Update the current AP if the supplicant notified a current BSS change * before it sent the current BSS's scan result. */ - if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0) + if (nm_supplicant_interface_get_current_bss (iface) == bss_info->bss_path) supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self); schedule_ap_list_dump (self); } static void -supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, - const char *object_path, - NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv; - NMWifiAP *ap; - - g_return_if_fail (self != NULL); - g_return_if_fail (object_path != NULL); - - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path); - if (!ap) - return; - - if (ap == priv->current_ap) { - /* The current AP cannot be removed (to prevent NM indicating that - * it is connected, but to nothing), but it must be removed later - * when the current AP is changed or cleared. Set 'fake' to - * indicate that this AP is now unknown to the supplicant. - */ - if (nm_wifi_ap_set_fake (ap, TRUE)) - _ap_dump (self, LOGL_DEBUG, ap, "updated", 0); - } else { - ap_add_remove (self, FALSE, ap, TRUE); - schedule_ap_list_dump (self); - } -} - -static void cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); @@ -2029,50 +2033,71 @@ reacquire_interface_cb (gpointer user_data) } static void -supplicant_iface_state_cb (NMSupplicantInterface *iface, - int new_state_i, - int old_state_i, - int disconnect_reason, - gpointer user_data) +supplicant_iface_state_down (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + cleanup_association_attempt (self, FALSE); + + /* If the device is already in UNAVAILABLE state then the state change + * is a NOP and the interface won't be re-acquired in the device state + * change handler. So ensure we have a new one here so that we're + * ready if the supplicant comes back. + */ + supplicant_interface_release (self); + if (priv->failed_iface_count < 5) + priv->reacquire_iface_id = g_timeout_add_seconds (10, reacquire_interface_cb, self); + else + _LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface keeps failing, giving up"); +} + +static void +supplicant_iface_state (NMDeviceWifi *self, + NMSupplicantInterfaceState new_state, + NMSupplicantInterfaceState old_state, + int disconnect_reason, + gboolean is_real_signal) { - NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); NMDeviceState devstate; gboolean scanning; - NMSupplicantInterfaceState new_state = new_state_i; - NMSupplicantInterfaceState old_state = old_state_i; - - if (new_state == old_state) - return; _LOGI (LOGD_DEVICE | LOGD_WIFI, - "supplicant interface state: %s -> %s", + "supplicant interface state: %s -> %s%s", nm_supplicant_interface_state_to_string (old_state), - nm_supplicant_interface_state_to_string (new_state)); + nm_supplicant_interface_state_to_string (new_state), + is_real_signal ? "" : " (simulated signal)"); + + if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + supplicant_iface_state_down (self); + goto out; + } devstate = nm_device_get_state (device); - scanning = nm_supplicant_interface_get_scanning (iface); + scanning = nm_supplicant_interface_get_scanning (priv->sup_iface); + + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + _LOGD (LOGD_WIFI, "supplicant ready"); + nm_device_queue_recheck_available (NM_DEVICE (device), + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + priv->scan_interval = SCAN_INTERVAL_MIN; + } /* In these states we know the supplicant is actually talking to something */ if ( new_state >= NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) priv->ssid_found = TRUE; - if ( old_state < NM_SUPPLICANT_INTERFACE_STATE_READY - && new_state >= NM_SUPPLICANT_INTERFACE_STATE_READY) + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) recheck_p2p_availability (self); switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: - _LOGD (LOGD_WIFI, "supplicant ready"); - nm_device_queue_recheck_available (NM_DEVICE (device), - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - priv->scan_interval = SCAN_INTERVAL_MIN; - if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) - nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); - break; case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); @@ -2127,26 +2152,6 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, } } break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: - nm_device_queue_recheck_available (NM_DEVICE (device), - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - cleanup_association_attempt (self, FALSE); - - if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) - nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); - - /* If the device is already in UNAVAILABLE state then the state change - * is a NOP and the interface won't be re-acquired in the device state - * change handler. So ensure we have a new one here so that we're - * ready if the supplicant comes back. - */ - supplicant_interface_release (self); - if (priv->failed_iface_count < 5) - priv->reacquire_iface_id = g_timeout_add_seconds (10, reacquire_interface_cb, self); - else - _LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface keeps failing, giving up"); - break; case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE: /* we would clear _requested_scan_set() and trigger a new scan. * However, we don't want to cancel the current pending action, so force @@ -2157,10 +2162,25 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, break; } - /* Signal scanning state changes */ - if ( new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING - || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) - _notify_scanning (self); +out: + _notify_scanning (self); + + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) + nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); +} + +static void +supplicant_iface_state_cb (NMSupplicantInterface *iface, + int new_state_i, + int old_state_i, + int disconnect_reason, + gpointer user_data) +{ + supplicant_iface_state (user_data, + new_state_i, + old_state_i, + disconnect_reason, + TRUE); } static void @@ -2197,12 +2217,12 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - const char *current_bss; + NMRefString *current_bss; NMWifiAP *new_ap = NULL; current_bss = nm_supplicant_interface_get_current_bss (iface); if (current_bss) - new_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, current_bss); + new_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, current_bss->str); if (new_ap != priv->current_ap) { const char *new_bssid = NULL; @@ -2293,11 +2313,8 @@ supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self) { - /* Do not update when the interface is still initializing. */ - if (nm_supplicant_interface_get_state (iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) - return; - - recheck_p2p_availability (self); + if (nm_supplicant_interface_get_state (iface) > NM_SUPPLICANT_INTERFACE_STATE_STARTING) + recheck_p2p_availability (self); } static gboolean @@ -3058,8 +3075,7 @@ device_state_changed (NMDevice *device, /* Clean up the supplicant interface because in these states the * device cannot be used. */ - if (priv->sup_iface) - supplicant_interface_release (self); + supplicant_interface_release (self); nm_clear_g_source (&priv->periodic_source_id); @@ -3160,8 +3176,7 @@ set_enabled (NMDevice *device, gboolean enabled) /* Re-initialize the supplicant interface and wait for it to be ready */ cleanup_supplicant_failures (self); - if (priv->sup_iface) - supplicant_interface_release (self); + supplicant_interface_release (self); supplicant_interface_acquire (self); _LOGD (LOGD_WIFI, "enable waiting on supplicant state"); diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index 18f1a5a910..ef0c70cc7f 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -10,15 +10,16 @@ #include <stdlib.h> -#include "nm-setting-wireless.h" - -#include "nm-wifi-utils.h" #include "NetworkManagerUtils.h" -#include "nm-utils.h" -#include "nm-core-internal.h" -#include "platform/nm-platform.h" #include "devices/nm-device.h" +#include "nm-core-internal.h" #include "nm-dbus-manager.h" +#include "nm-glib-aux/nm-ref-string.h" +#include "nm-setting-wireless.h" +#include "nm-utils.h" +#include "nm-wifi-utils.h" +#include "platform/nm-platform.h" +#include "supplicant/nm-supplicant-interface.h" #define PROTO_WPA "wpa" #define PROTO_RSN "rsn" @@ -39,7 +40,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiAP, ); struct _NMWifiAPPrivate { - char *supplicant_path; /* D-Bus object path of this AP from wpa_supplicant */ + NMRefString *supplicant_path; /* Scanned or cached values */ GBytes * ssid; @@ -49,6 +50,9 @@ struct _NMWifiAPPrivate { guint32 freq; /* Frequency in MHz; ie 2412 (== 2.412 GHz) */ guint32 max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */ + gint64 last_seen_msec; /* Timestamp when the AP was seen lastly (in nm_utils_get_monotonic_timestamp_*() scale). + * Note that this value might be negative! */ + NM80211ApFlags flags; /* General flags */ NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */ NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */ @@ -58,7 +62,6 @@ struct _NMWifiAPPrivate { /* Non-scanned attributes */ bool fake:1; /* Whether or not the AP is from a scan */ bool hotspot:1; /* Whether the AP is a local device's hotspot network */ - gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_sec()) */ }; typedef struct _NMWifiAPPrivate NMWifiAPPrivate; @@ -78,7 +81,7 @@ nm_wifi_ap_get_supplicant_path (NMWifiAP *ap) { g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL); - return NM_WIFI_AP_GET_PRIVATE (ap)->supplicant_path; + return nm_ref_string_get_str (NM_WIFI_AP_GET_PRIVATE (ap)->supplicant_path); } GBytes * @@ -148,11 +151,7 @@ nm_wifi_ap_set_ssid (NMWifiAP *ap, GBytes *ssid) static gboolean nm_wifi_ap_set_flags (NMWifiAP *ap, NM80211ApFlags flags) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); - - priv = NM_WIFI_AP_GET_PRIVATE (ap); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); if (priv->flags != flags) { priv->flags = flags; @@ -165,11 +164,8 @@ nm_wifi_ap_set_flags (NMWifiAP *ap, NM80211ApFlags flags) static gboolean nm_wifi_ap_set_wpa_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); - priv = NM_WIFI_AP_GET_PRIVATE (ap); if (priv->wpa_flags != flags) { priv->wpa_flags = flags; _notify (ap, PROP_WPA_FLAGS); @@ -181,11 +177,8 @@ nm_wifi_ap_set_wpa_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags) static gboolean nm_wifi_ap_set_rsn_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); - priv = NM_WIFI_AP_GET_PRIVATE (ap); if (priv->rsn_flags != flags) { priv->rsn_flags = flags; _notify (ap, PROP_RSN_FLAGS); @@ -203,11 +196,9 @@ nm_wifi_ap_get_address (const NMWifiAP *ap) } static gboolean -nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 *addr /* ETH_ALEN bytes */) +nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 addr[static 6 /* ETH_ALEN */]) { - NMWifiAPPrivate *priv; - - priv = NM_WIFI_AP_GET_PRIVATE (ap); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); if ( !priv->address || !nm_utils_hwaddr_matches (addr, ETH_ALEN, priv->address, -1)) { @@ -241,16 +232,14 @@ nm_wifi_ap_get_mode (NMWifiAP *ap) } static gboolean -nm_wifi_ap_set_mode (NMWifiAP *ap, const NM80211Mode mode) +nm_wifi_ap_set_mode (NMWifiAP *ap, NM80211Mode mode) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); - g_return_val_if_fail ( mode == NM_802_11_MODE_ADHOC - || mode == NM_802_11_MODE_INFRA - || mode == NM_802_11_MODE_MESH, FALSE); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); - priv = NM_WIFI_AP_GET_PRIVATE (ap); + nm_assert (NM_IN_SET (mode, NM_802_11_MODE_UNKNOWN, + NM_802_11_MODE_ADHOC, + NM_802_11_MODE_INFRA, + NM_802_11_MODE_MESH)); if (priv->mode != mode) { priv->mode = mode; @@ -277,13 +266,9 @@ nm_wifi_ap_get_strength (NMWifiAP *ap) } gboolean -nm_wifi_ap_set_strength (NMWifiAP *ap, const gint8 strength) +nm_wifi_ap_set_strength (NMWifiAP *ap, gint8 strength) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); - - priv = NM_WIFI_AP_GET_PRIVATE (ap); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); if (priv->strength != strength) { priv->strength = strength; @@ -303,13 +288,9 @@ nm_wifi_ap_get_freq (NMWifiAP *ap) gboolean nm_wifi_ap_set_freq (NMWifiAP *ap, - const guint32 freq) + guint32 freq) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); - - priv = NM_WIFI_AP_GET_PRIVATE (ap); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); if (priv->freq != freq) { priv->freq = freq; @@ -378,16 +359,12 @@ nm_wifi_ap_get_flags (const NMWifiAP *ap) } static gboolean -nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen) +nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen_msec) { - NMWifiAPPrivate *priv; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); - - priv = NM_WIFI_AP_GET_PRIVATE (ap); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); - if (priv->last_seen != last_seen) { - priv->last_seen = last_seen; + if (priv->last_seen_msec != last_seen_msec) { + priv->last_seen_msec = last_seen_msec; _notify (ap, PROP_LAST_SEEN); return TRUE; } @@ -402,180 +379,53 @@ nm_wifi_ap_get_metered (const NMWifiAP *self) /*****************************************************************************/ -static NM80211ApSecurityFlags -security_from_vardict (GVariant *security) -{ - NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; - const char **array, *tmp; - - g_return_val_if_fail (g_variant_is_of_type (security, G_VARIANT_TYPE_VARDICT), NM_802_11_AP_SEC_NONE); - - if ( g_variant_lookup (security, "KeyMgmt", "^a&s", &array) - && array) { - if (g_strv_contains (array, "wpa-psk") || - g_strv_contains (array, "wpa-ft-psk")) - flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; - if (g_strv_contains (array, "wpa-eap") || - g_strv_contains (array, "wpa-ft-eap") || - g_strv_contains (array, "wpa-fils-sha256") || - g_strv_contains (array, "wpa-fils-sha384")) - flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; - if (g_strv_contains (array, "sae")) - flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE; - if (g_strv_contains (array, "owe")) - flags |= NM_802_11_AP_SEC_KEY_MGMT_OWE; - g_free (array); - } - - if ( g_variant_lookup (security, "Pairwise", "^a&s", &array) - && array) { - if (g_strv_contains (array, "tkip")) - flags |= NM_802_11_AP_SEC_PAIR_TKIP; - if (g_strv_contains (array, "ccmp")) - flags |= NM_802_11_AP_SEC_PAIR_CCMP; - g_free (array); - } - - if (g_variant_lookup (security, "Group", "&s", &tmp)) { - if (strcmp (tmp, "wep40") == 0) - flags |= NM_802_11_AP_SEC_GROUP_WEP40; - if (strcmp (tmp, "wep104") == 0) - flags |= NM_802_11_AP_SEC_GROUP_WEP104; - if (strcmp (tmp, "tkip") == 0) - flags |= NM_802_11_AP_SEC_GROUP_TKIP; - if (strcmp (tmp, "ccmp") == 0) - flags |= NM_802_11_AP_SEC_GROUP_CCMP; - } - - return flags; -} - -/*****************************************************************************/ - gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap, - const char *supplicant_path, - GVariant *properties) + const NMSupplicantBssInfo *bss_info) { NMWifiAPPrivate *priv; - const guint8 *bytes; - GVariant *v; - gsize len; - gsize i; - gboolean b = FALSE; - const char *s; - gint16 i16; - guint16 u16; gboolean changed = FALSE; - gboolean metered; - guint32 max_rate, rate; g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); - g_return_val_if_fail (properties, FALSE); + g_return_val_if_fail (bss_info, FALSE); + nm_assert (NM_IS_REF_STRING (bss_info->bss_path)); priv = NM_WIFI_AP_GET_PRIVATE (ap); - g_object_freeze_notify (G_OBJECT (ap)); - - if (g_variant_lookup (properties, "Privacy", "b", &b) && b) - changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY); - - v = g_variant_lookup_value (properties, "WPS", G_VARIANT_TYPE_VARDICT); - if (v) { - if (g_variant_lookup (v, "Type", "&s", &s)) { - changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS); - if (strcmp (s, "pbc") == 0) - changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PBC); - else if (strcmp (s, "pin") == 0) - changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PIN); - } - g_variant_unref (v); - } - - if (g_variant_lookup (properties, "Mode", "&s", &s)) { - if (!g_strcmp0 (s, "infrastructure")) - changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA); - else if (!g_strcmp0 (s, "ad-hoc")) - changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC); - else if (!g_strcmp0 (s, "mesh")) - changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_MESH); - } - - if (g_variant_lookup (properties, "Signal", "n", &i16)) - changed |= nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (i16)); - - if (g_variant_lookup (properties, "Frequency", "q", &u16)) - changed |= nm_wifi_ap_set_freq (ap, u16); - - v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); - if (v) { - bytes = g_variant_get_fixed_array (v, &len, 1); - len = MIN (32, len); - - /* Stupid ieee80211 layer uses <hidden> */ - if ( bytes - && len - && !( NM_IN_SET (len, 8, 9) - && memcmp (bytes, "<hidden>", len) == 0) - && !nm_utils_is_empty_ssid (bytes, len)) { - /* good */ - } else - len = 0; + nm_assert ( !priv->supplicant_path + || priv->supplicant_path == bss_info->bss_path); - changed |= nm_wifi_ap_set_ssid_arr (ap, bytes, len); - - g_variant_unref (v); - } + g_object_freeze_notify (G_OBJECT (ap)); - v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); - if (v) { - bytes = g_variant_get_fixed_array (v, &len, 1); - if ( len == ETH_ALEN - && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 - && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) - changed |= nm_wifi_ap_set_address_bin (ap, bytes); - g_variant_unref (v); + if (!priv->supplicant_path) { + priv->supplicant_path = nm_ref_string_ref (bss_info->bss_path); + changed = TRUE; } - max_rate = 0; - v = g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au")); - if (v) { - const guint32 *rates = g_variant_get_fixed_array (v, &len, sizeof (guint32)); + changed |= nm_wifi_ap_set_flags (ap, bss_info->ap_flags); + changed |= nm_wifi_ap_set_mode (ap, bss_info->mode); + changed |= nm_wifi_ap_set_strength (ap, bss_info->signal_percent); + changed |= nm_wifi_ap_set_freq (ap, bss_info->frequency); + changed |= nm_wifi_ap_set_ssid (ap, bss_info->ssid); - for (i = 0; i < len; i++) - max_rate = NM_MAX (max_rate, rates[i]); - g_variant_unref (v); - } - v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); - if (v) { - bytes = g_variant_get_fixed_array (v, &len, 1); - nm_wifi_utils_parse_ies (bytes, len, &rate, &metered); - max_rate = NM_MAX (max_rate, rate); - g_variant_unref (v); - priv->metered = metered; + if (bss_info->bssid_valid) + changed |= nm_wifi_ap_set_address_bin (ap, bss_info->bssid); + else { + /* we don't actually clear the value. */ } - if (max_rate) - changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000); + changed |= nm_wifi_ap_set_max_bitrate (ap, bss_info->max_rate); - v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT); - if (v) { - changed |= nm_wifi_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v)); - g_variant_unref (v); + if (priv->metered != bss_info->metered) { + priv->metered = bss_info->metered; + changed = TRUE; } - v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT); - if (v) { - changed |= nm_wifi_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v)); - g_variant_unref (v); - } + changed |= nm_wifi_ap_set_wpa_flags (ap, bss_info->wpa_flags); + changed |= nm_wifi_ap_set_rsn_flags (ap, bss_info->rsn_flags); - if (!priv->supplicant_path) { - priv->supplicant_path = g_strdup (supplicant_path); - changed = TRUE; - } + changed |= nm_wifi_ap_set_last_seen (ap, bss_info->last_seen_msec); - changed |= nm_wifi_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_sec ()); changed |= nm_wifi_ap_set_fake (ap, FALSE); g_object_thaw_notify (G_OBJECT (ap)); @@ -674,9 +524,10 @@ nm_wifi_ap_to_string (const NMWifiAP *self, g_return_val_if_fail (NM_IS_WIFI_AP (self), NULL); priv = NM_WIFI_AP_GET_PRIVATE (self); + chan = nm_utils_wifi_freq_to_channel (priv->freq); if (priv->supplicant_path) - supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id; + supplicant_id = strrchr (priv->supplicant_path->str, '/') ?: supplicant_id; export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); if (export_path) @@ -703,7 +554,9 @@ nm_wifi_ap_to_string (const NMWifiAP *self, priv->metered ? 'M' : '_', priv->wpa_flags & 0xFFFF, priv->rsn_flags & 0xFFFF, - priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_sec ()) - priv->last_seen) : -1, + priv->last_seen_msec != G_MININT64 + ? (int) ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_sec ()) - (priv->last_seen_msec / 1000)) + : -1, supplicant_id, export_path); return str_buf; @@ -856,9 +709,9 @@ get_property (GObject *object, guint prop_id, break; case PROP_LAST_SEEN: g_value_set_int (value, - priv->last_seen > 0 - ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NSEC_PER_SEC) - : -1); + priv->last_seen_msec != G_MININT64 + ? (int) NM_MAX (nm_utils_monotonic_timestamp_as_boottime (priv->last_seen_msec, NM_UTILS_NSEC_PER_MSEC) / 1000, 1) + : -1); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -883,26 +736,16 @@ nm_wifi_ap_init (NMWifiAP *self) priv->flags = NM_802_11_AP_FLAGS_NONE; priv->wpa_flags = NM_802_11_AP_SEC_NONE; priv->rsn_flags = NM_802_11_AP_SEC_NONE; - priv->last_seen = -1; + priv->last_seen_msec = G_MININT64; } NMWifiAP * -nm_wifi_ap_new_from_properties (const char *supplicant_path, GVariant *properties) +nm_wifi_ap_new_from_properties (const NMSupplicantBssInfo *bss_info) { NMWifiAP *ap; - g_return_val_if_fail (supplicant_path != NULL, NULL); - g_return_val_if_fail (properties != NULL, NULL); - - ap = (NMWifiAP *) g_object_new (NM_TYPE_WIFI_AP, NULL); - nm_wifi_ap_update_from_properties (ap, supplicant_path, properties); - - /* ignore APs with invalid or missing BSSIDs */ - if (!nm_wifi_ap_get_address (ap)) { - g_object_unref (ap); - return NULL; - } - + ap = g_object_new (NM_TYPE_WIFI_AP, NULL); + nm_wifi_ap_update_from_properties (ap, bss_info); return ap; } @@ -1028,7 +871,7 @@ finalize (GObject *object) nm_assert (!self->wifi_device); nm_assert (c_list_is_empty (&self->aps_lst)); - g_free (priv->supplicant_path); + nm_ref_string_unref (priv->supplicant_path); if (priv->ssid) g_bytes_unref (priv->ssid); g_free (priv->address); diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index 472dfdf908..f9d258b06c 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -36,17 +36,17 @@ typedef struct { struct _NMWifiAPPrivate *_priv; } NMWifiAP; +struct _NMSupplicantBssInfo; + typedef struct _NMWifiAPClass NMWifiAPClass; GType nm_wifi_ap_get_type (void); -NMWifiAP * nm_wifi_ap_new_from_properties (const char *supplicant_path, - GVariant *properties); -NMWifiAP * nm_wifi_ap_new_fake_from_connection (NMConnection *connection); +NMWifiAP *nm_wifi_ap_new_from_properties (const struct _NMSupplicantBssInfo *bss_info); +NMWifiAP *nm_wifi_ap_new_fake_from_connection (NMConnection *connection); -gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap, - const char *supplicant_path, - GVariant *properties); +gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap, + const struct _NMSupplicantBssInfo *bss_info); gboolean nm_wifi_ap_check_compatible (NMWifiAP *self, NMConnection *connection); diff --git a/src/devices/wifi/nm-wifi-p2p-peer.c b/src/devices/wifi/nm-wifi-p2p-peer.c index 51f5a505ae..c58bd95e29 100644 --- a/src/devices/wifi/nm-wifi-p2p-peer.c +++ b/src/devices/wifi/nm-wifi-p2p-peer.c @@ -9,15 +9,16 @@ #include <stdlib.h> -#include "nm-setting-wireless.h" - -#include "nm-wifi-utils.h" #include "NetworkManagerUtils.h" -#include "nm-utils.h" -#include "nm-core-internal.h" -#include "platform/nm-platform.h" #include "devices/nm-device.h" +#include "nm-core-internal.h" #include "nm-dbus-manager.h" +#include "nm-glib-aux/nm-ref-string.h" +#include "nm-setting-wireless.h" +#include "nm-utils.h" +#include "nm-wifi-utils.h" +#include "platform/nm-platform.h" +#include "supplicant/nm-supplicant-types.h" /*****************************************************************************/ @@ -35,7 +36,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiP2PPeer, ); struct _NMWifiP2PPeerPrivate { - char *supplicant_path; /* D-Bus object path of this Peer from wpa_supplicant */ + NMRefString *supplicant_path; /* D-Bus object path of this Peer from wpa_supplicant */ /* Scanned or cached values */ char * name; @@ -150,7 +151,7 @@ nm_wifi_p2p_peer_get_supplicant_path (NMWifiP2PPeer *peer) { g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); - return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->supplicant_path; + return nm_ref_string_get_str (NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->supplicant_path); } const char * @@ -372,69 +373,42 @@ nm_wifi_p2p_peer_set_last_seen (NMWifiP2PPeer *peer, gint32 last_seen) gboolean nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, - const char *supplicant_path, - GVariant *properties) + const NMSupplicantPeerInfo *peer_info) { NMWifiP2PPeerPrivate *priv; - const guint8 *bytes; - GVariant *v; - gsize len; - const char *s; - gint32 i32; gboolean changed = FALSE; g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); - g_return_val_if_fail (properties, FALSE); + g_return_val_if_fail (peer_info, FALSE); + nm_assert (NM_IS_REF_STRING (peer_info->peer_path)); priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); - g_object_freeze_notify (G_OBJECT (peer)); - - if (g_variant_lookup (properties, "level", "i", &i32)) - changed |= nm_wifi_p2p_peer_set_strength (peer, nm_wifi_utils_level_to_quality (i32)); - - if (g_variant_lookup (properties, "DeviceName", "&s", &s)) - changed |= nm_wifi_p2p_peer_set_name (peer, s); - - if (g_variant_lookup (properties, "Manufacturer", "&s", &s)) - changed |= nm_wifi_p2p_peer_set_manufacturer (peer, s); + nm_assert ( !priv->supplicant_path + || priv->supplicant_path == peer_info->peer_path); - if (g_variant_lookup (properties, "Model", "&s", &s)) - changed |= nm_wifi_p2p_peer_set_model (peer, s); - - if (g_variant_lookup (properties, "ModelNumber", "&s", &s)) - changed |= nm_wifi_p2p_peer_set_model_number (peer, s); - - if (g_variant_lookup (properties, "Serial", "&s", &s)) - changed |= nm_wifi_p2p_peer_set_serial (peer, s); - - v = g_variant_lookup_value (properties, "DeviceAddress", G_VARIANT_TYPE_BYTESTRING); - if (v) { - bytes = g_variant_get_fixed_array (v, &len, 1); - if ( len == ETH_ALEN - && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 - && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) - changed |= nm_wifi_p2p_peer_set_address_bin (peer, bytes); - g_variant_unref (v); - } - - /* The IEs property contains the WFD R1 subelements */ - v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); - if (v) { - gs_unref_bytes GBytes *b = NULL; - - bytes = g_variant_get_fixed_array (v, &len, 1); - b = g_bytes_new (bytes, len); - changed |= nm_wifi_p2p_peer_set_wfd_ies (peer, b); - g_variant_unref (v); - } + g_object_freeze_notify (G_OBJECT (peer)); if (!priv->supplicant_path) { - priv->supplicant_path = g_strdup (supplicant_path); + priv->supplicant_path = nm_ref_string_ref (peer_info->peer_path); changed = TRUE; } - changed |= nm_wifi_p2p_peer_set_last_seen (peer, nm_utils_get_monotonic_timestamp_sec ()); + changed |= nm_wifi_p2p_peer_set_strength (peer, peer_info->signal_percent); + changed |= nm_wifi_p2p_peer_set_name (peer, peer_info->device_name); + changed |= nm_wifi_p2p_peer_set_manufacturer (peer, peer_info->manufacturer); + changed |= nm_wifi_p2p_peer_set_model (peer, peer_info->model); + changed |= nm_wifi_p2p_peer_set_model_number (peer, peer_info->model_number); + changed |= nm_wifi_p2p_peer_set_serial (peer, peer_info->serial); + + if (peer_info->address_valid) + changed |= nm_wifi_p2p_peer_set_address_bin (peer, peer_info->address); + else { + /* we don't reset the address. */ + } + + changed |= nm_wifi_p2p_peer_set_wfd_ies (peer, peer_info->ies); + changed |= nm_wifi_p2p_peer_set_last_seen (peer, peer_info->last_seen_msec / 1000u); g_object_thaw_notify (G_OBJECT (peer)); @@ -456,7 +430,7 @@ nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self, priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); if (priv->supplicant_path) - supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id; + supplicant_id = strrchr (priv->supplicant_path->str, '/') ?: supplicant_id; export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); if (export_path) @@ -572,22 +546,14 @@ nm_wifi_p2p_peer_init (NMWifiP2PPeer *self) } NMWifiP2PPeer * -nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, GVariant *properties) +nm_wifi_p2p_peer_new_from_properties (const NMSupplicantPeerInfo *peer_info) { NMWifiP2PPeer *peer; - g_return_val_if_fail (supplicant_path != NULL, NULL); - g_return_val_if_fail (properties != NULL, NULL); - - peer = (NMWifiP2PPeer *) g_object_new (NM_TYPE_WIFI_P2P_PEER, NULL); - nm_wifi_p2p_peer_update_from_properties (peer, supplicant_path, properties); - - /* ignore peers with invalid or missing address */ - if (!nm_wifi_p2p_peer_get_address (peer)) { - g_object_unref (peer); - return NULL; - } + g_return_val_if_fail (peer_info, NULL); + peer = g_object_new (NM_TYPE_WIFI_P2P_PEER, NULL); + nm_wifi_p2p_peer_update_from_properties (peer, peer_info); return peer; } @@ -600,7 +566,7 @@ finalize (GObject *object) nm_assert (!self->wifi_device); nm_assert (c_list_is_empty (&self->peers_lst)); - g_free (priv->supplicant_path); + nm_ref_string_unref (priv->supplicant_path); g_free (priv->name); g_free (priv->manufacturer); g_free (priv->model); diff --git a/src/devices/wifi/nm-wifi-p2p-peer.h b/src/devices/wifi/nm-wifi-p2p-peer.h index e84789c094..59f9c5d269 100644 --- a/src/devices/wifi/nm-wifi-p2p-peer.h +++ b/src/devices/wifi/nm-wifi-p2p-peer.h @@ -37,14 +37,14 @@ typedef struct { typedef struct _NMWifiP2PPeerClass NMWifiP2PPeerClass; +struct _NMSupplicantPeerInfo; + GType nm_wifi_p2p_peer_get_type (void); -NMWifiP2PPeer * nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, - GVariant *properties); +NMWifiP2PPeer *nm_wifi_p2p_peer_new_from_properties (const struct _NMSupplicantPeerInfo *peer_info); -gboolean nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, - const char *supplicant_path, - GVariant *properties); +gboolean nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, + const struct _NMSupplicantPeerInfo *peer_info); gboolean nm_wifi_p2p_peer_check_compatible (NMWifiP2PPeer *self, NMConnection *connection); diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 04f35e4182..6edeef682a 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -7,39 +7,35 @@ #include "nm-default.h" #include "nm-supplicant-interface.h" -#include "nm-supplicant-manager.h" #include <stdio.h> #include "NetworkManagerUtils.h" -#include "nm-supplicant-config.h" #include "nm-core-internal.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-ref-string.h" #include "nm-std-aux/nm-dbus-compat.h" +#include "nm-supplicant-config.h" +#include "nm-supplicant-manager.h" +#include "shared/nm-glib-aux/nm-dbus-aux.h" -/*****************************************************************************/ - -typedef struct { - GDBusProxy *proxy; - gulong change_id; -} BssData; - -typedef struct { - GDBusProxy *proxy; - gulong change_id; -} PeerData; +#define DBUS_TIMEOUT_MSEC 20000 -struct _AddNetworkData; +/*****************************************************************************/ typedef struct { NMSupplicantInterface *self; char *type; char *bssid; char *pin; - GDBusProxy *proxy; + guint signal_id; GCancellable *cancellable; - bool is_cancelling; + bool needs_cancelling:1; + bool is_cancelling:1; } WpsData; +struct _AddNetworkData; + typedef struct { NMSupplicantInterface *self; NMSupplicantConfig *cfg; @@ -54,32 +50,28 @@ typedef struct { typedef struct _AddNetworkData { /* the assoc_data at the time when doing the call. */ AssocData *assoc_data; + NMRefString *name_owner; + NMRefString *object_path; + GObject *shutdown_wait_obj; } AddNetworkData; -typedef struct { - NMSupplicantInterface *self; - NMSupplicantInterfaceDisconnectCb callback; - gpointer user_data; -} DisconnectData; - enum { STATE, /* change in the interface's state */ - REMOVED, /* interface was removed by the supplicant */ - BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ - BSS_REMOVED, /* supplicant removed BSS from its scan list */ - PEER_UPDATED, /* a new Peer appeared or an existing had properties changed */ - PEER_REMOVED, /* supplicant removed Peer from its scan list */ + BSS_CHANGED, /* a new BSS appeared, was updated, or was removed. */ + PEER_CHANGED, /* a new Peer appeared, was updated, or was removed */ SCAN_DONE, /* wifi scan is complete */ WPS_CREDENTIALS, /* WPS credentials received */ GROUP_STARTED, /* a new Group (interface) was created */ GROUP_FINISHED, /* a Group (interface) has been finished */ LAST_SIGNAL }; + static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, - PROP_IFACE, - PROP_OBJECT_PATH, + PROP_SUPPLICANT_MANAGER, + PROP_DBUS_OBJECT_PATH, + PROP_IFINDEX, PROP_P2P_GROUP_JOINED, PROP_P2P_GROUP_PATH, PROP_P2P_GROUP_OWNER, @@ -87,53 +79,76 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_CURRENT_BSS, PROP_DRIVER, PROP_P2P_AVAILABLE, - PROP_GLOBAL_CAPABILITIES, PROP_AUTH_STATE, ); typedef struct _NMSupplicantInterfacePrivate { - char * dev; - NMSupplicantDriver driver; - NMSupplCapMask global_capabilities; - NMSupplCapMask iface_capabilities; - guint32 max_scan_ssids; - guint32 ready_count; - char * object_path; - NMSupplicantInterfaceState state; - int disconnect_reason; + NMSupplicantManager *supplicant_manager; - GDBusProxy * wpas_proxy; - GCancellable * init_cancellable; - GDBusProxy * iface_proxy; - GCancellable * other_cancellable; - GDBusProxy * p2p_proxy; - GDBusProxy * group_proxy; + GDBusConnection *dbus_connection; + NMRefString *name_owner; + NMRefString *object_path; - WpsData *wps_data; + char *ifname; + + GCancellable *main_cancellable; + + NMRefString *p2p_group_path; - AssocData * assoc_data; + GCancellable *p2p_group_properties_cancellable; - char * net_path; - GHashTable * bss_proxies; - char * current_bss; + WpsData *wps_data; - GHashTable * peer_proxies; + AssocData *assoc_data; - gint64 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_msec() */ + char *net_path; + + char *driver; + + GHashTable *bss_idx; + CList bss_lst_head; + CList bss_initializing_lst_head; + + NMRefString *current_bss; + + GHashTable *peer_idx; + CList peer_lst_head; + CList peer_initializing_lst_head; + + gint64 last_scan_msec; NMSupplicantAuthState auth_state; - bool scanning:1; + NMSupplicantDriver requested_driver; + NMSupplCapMask global_capabilities; + NMSupplCapMask iface_capabilities; - bool scan_done_pending:1; - bool scan_done_success:1; + guint properties_changed_id; + guint signal_id; + guint bss_properties_changed_id; + guint peer_properties_changed_id; + guint p2p_group_properties_changed_id; + + int ifindex; + + int starting_pending_count; + + guint32 max_scan_ssids; + + gint32 disconnect_reason; + + NMSupplicantInterfaceState state; + NMSupplicantInterfaceState supp_state; + + bool scanning:1; - bool p2p_proxy_acquired:1; - bool group_proxy_acquired:1; bool p2p_capable:1; - bool p2p_group_owner:1; + bool p2p_group_is_owner:1; + + bool is_ready_main:1; + bool is_ready_p2p_device:1; } NMSupplicantInterfacePrivate; @@ -147,43 +162,73 @@ G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT) /*****************************************************************************/ +static const char * +_log_pretty_object_path (NMSupplicantInterfacePrivate *priv) +{ + const char *s; + + nm_assert (priv); + nm_assert (NM_IS_REF_STRING (priv->object_path)); + + s = priv->object_path->str; + if (NM_STR_HAS_PREFIX (s, "/fi/w1/wpa_supplicant1/Interfaces/")) { + s += NM_STRLEN ("/fi/w1/wpa_supplicant1/Interfaces/"); + if ( s[0] + && s[0] != '/') + return s; + } + return priv->object_path->str; +} + #define _NMLOG_DOMAIN LOGD_SUPPLICANT #define _NMLOG_PREFIX_NAME "sup-iface" #define _NMLOG(level, ...) \ G_STMT_START { \ - char _sbuf[64]; \ - const char *__ifname = self ? NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev : NULL; \ + NMSupplicantInterface *_self = (self); \ + NMSupplicantInterfacePrivate *_priv = _self ? NM_SUPPLICANT_INTERFACE_GET_PRIVATE (_self) : NULL; \ + char _sbuf[255]; \ + const char *_ifname = _priv ? _priv->ifname : NULL; \ \ - nm_log ((level), _NMLOG_DOMAIN, __ifname, NULL, \ + nm_log ((level), _NMLOG_DOMAIN, _ifname, NULL, \ "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ - ((self) ? nm_sprintf_buf (_sbuf, "[%p,%s]", (self), __ifname) : "") \ + ( _self \ + ? nm_sprintf_buf (_sbuf, \ + "["NM_HASH_OBFUSCATE_PTR_FMT",%s,%s]", \ + NM_HASH_OBFUSCATE_PTR (_self), \ + _log_pretty_object_path (_priv), \ + _ifname ?: "???") \ + : "") \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END /*****************************************************************************/ -static void scan_done_emit_signal (NMSupplicantInterface *self); +static void _starting_check_ready (NMSupplicantInterface *self); + +static void assoc_return (NMSupplicantInterface *self, + GError *error, + const char *message); /*****************************************************************************/ 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_DEFAULT_WARN ("internal-unknown"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID, "internal-invalid"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_STARTING, "internal-starting"), + + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE, "4way_handshake"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED, "associated"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING, "associating"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING, "authenticating"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_COMPLETED, "completed"), NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, "disconnected"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE, "group_handshake"), NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INACTIVE, "inactive"), + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISABLED, "interface_disabled"), 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"), + + NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DOWN, "internal-down"), ); static @@ -206,346 +251,847 @@ NM_UTILS_STRING_TABLE_LOOKUP_DEFINE ( /*****************************************************************************/ +static NM80211ApSecurityFlags +security_from_vardict (GVariant *security) +{ + NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; + const char **array; + const char *tmp; + + nm_assert (g_variant_is_of_type (security, G_VARIANT_TYPE_VARDICT)); + + if (g_variant_lookup (security, "KeyMgmt", "^a&s", &array)) { + if (g_strv_contains (array, "wpa-psk") || + g_strv_contains (array, "wpa-ft-psk")) + flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; + if (g_strv_contains (array, "wpa-eap") || + g_strv_contains (array, "wpa-ft-eap") || + g_strv_contains (array, "wpa-fils-sha256") || + g_strv_contains (array, "wpa-fils-sha384")) + flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; + if (g_strv_contains (array, "sae")) + flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE; + if (g_strv_contains (array, "owe")) + flags |= NM_802_11_AP_SEC_KEY_MGMT_OWE; + g_free (array); + } + + if (g_variant_lookup (security, "Pairwise", "^a&s", &array)) { + if (g_strv_contains (array, "tkip")) + flags |= NM_802_11_AP_SEC_PAIR_TKIP; + if (g_strv_contains (array, "ccmp")) + flags |= NM_802_11_AP_SEC_PAIR_CCMP; + g_free (array); + } + + if (g_variant_lookup (security, "Group", "&s", &tmp)) { + if (nm_streq (tmp, "wep40")) + flags |= NM_802_11_AP_SEC_GROUP_WEP40; + else if (nm_streq (tmp, "wep104")) + flags |= NM_802_11_AP_SEC_GROUP_WEP104; + else if (nm_streq (tmp, "tkip")) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + else if (nm_streq (tmp, "ccmp")) + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + } + + return flags; +} + +/*****************************************************************************/ + +/* Various conditions prevent _starting_check_ready() from completing. For example, + * bss_initializing_lst_head, peer_initializing_lst_head and p2p_group_properties_cancellable. + * At some places, these conditions might toggle, and it would seems we would have + * to call _starting_check_ready() at that point, to ensure we don't miss a state + * change that we are ready. However, these places are deep in the call stack and + * not suitable to perform this state change. Instead, the callers *MUST* have + * added their own starting_pending_count to delay _starting_check_ready(). + * + * Assert that is the case. */ +#define nm_assert_starting_has_pending_count(v) nm_assert ((v) > 0) + +/*****************************************************************************/ + static void -bss_data_destroy (gpointer user_data) +_dbus_connection_call (NMSupplicantInterface *self, + const char *interface_name, + const char *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - BssData *bss_data = user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - nm_clear_g_signal_handler (bss_data->proxy, &bss_data->change_id); - g_object_unref (bss_data->proxy); - g_slice_free (BssData, bss_data); + g_dbus_connection_call (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + callback, + user_data); } static void -bss_proxy_properties_changed_cb (GDBusProxy *proxy, - GVariant *changed_properties, - char **invalidated_properties, +_dbus_connection_call_simple_cb (GObject *source, + GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterface *self; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + const char *log_reason; + gs_free char *remote_error = NULL; + + nm_utils_user_data_unpack (user_data, &self, &log_reason); + + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if (nm_utils_error_is_cancelled (error)) + return; + + if (res) { + _LOGT ("call-%s: success", log_reason); + return; + } + + remote_error = g_dbus_error_get_remote_error (error); + if (!nm_streq0 (remote_error, "fi.w1.wpa_supplicant1.NotConnected")) { + g_dbus_error_strip_remote_error (error); + _LOGW ("call-%s: failed with %s", log_reason, error->message); + return; + } + + _LOGT ("call-%s: failed with %s", log_reason, error->message); +} + +static void +_dbus_connection_call_simple (NMSupplicantInterface *self, + const char *interface_name, + const char *method_name, + GVariant *parameters, + const GVariantType *reply_type, + const char *log_reason) +{ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (priv->scanning) - priv->last_scan = nm_utils_get_monotonic_timestamp_msec (); + _dbus_connection_call (self, + interface_name, + method_name, + parameters, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + priv->main_cancellable, + _dbus_connection_call_simple_cb, + nm_utils_user_data_pack (self, log_reason)); +} + +/*****************************************************************************/ - g_signal_emit (self, signals[BSS_UPDATED], 0, - g_dbus_proxy_get_object_path (proxy), - changed_properties); +static void +_emit_signal_state (NMSupplicantInterface *self, + NMSupplicantInterfaceState new_state, + NMSupplicantInterfaceState old_state, + gint32 disconnect_reason) +{ + g_signal_emit (self, + signals[STATE], + 0, + (int) new_state, + (int) old_state, + (int) disconnect_reason); } -static GVariant * -bss_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy) +/*****************************************************************************/ + +static void +_remove_network (NMSupplicantInterface *self) { - gs_strfreev char **properties = NULL; - GVariantBuilder builder; - char **iter; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + gs_free char *net_path = NULL; + + if (!priv->net_path) + return; + + net_path = g_steal_pointer (&priv->net_path); + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "RemoveNetwork", + g_variant_new ("(o)", net_path), + G_VARIANT_TYPE ("()"), + "remove-network"); +} + +/*****************************************************************************/ + +static gboolean +_prop_p2p_available_get (NMSupplicantInterfacePrivate *priv) +{ + return priv->is_ready_p2p_device + && priv->p2p_capable; +} + +/*****************************************************************************/ + +static void +_bss_info_destroy (NMSupplicantBssInfo *bss_info) +{ + c_list_unlink_stale (&bss_info->_bss_lst); + nm_clear_g_cancellable (&bss_info->_init_cancellable); + g_bytes_unref (bss_info->ssid); + nm_ref_string_unref (bss_info->bss_path); + nm_g_slice_free (bss_info); +} + +static void +_bss_info_changed_emit (NMSupplicantInterface *self, + NMSupplicantBssInfo *bss_info, + gboolean is_present) +{ + g_signal_emit (self, + signals[BSS_CHANGED], + 0, + bss_info, + is_present); +} + +static void +_bss_info_properties_changed (NMSupplicantInterface *self, + NMSupplicantBssInfo *bss_info, + GVariant *properties, + gboolean initial) +{ + gboolean v_b; + GVariant *v_v; + const char *v_s; + gint16 v_i16; + guint16 v_u16; + guint32 v_u32; + NM80211ApFlags p_ap_flags; + NM80211Mode p_mode; + guint8 p_signal_percent; + const guint8 *arr_data; + gsize arr_len; + gboolean p_metered; + guint32 p_max_rate; + gboolean p_max_rate_has; + gint64 now_msec = 0; + + if (nm_g_variant_lookup (properties, "Age", "u", &v_u32)) { + bss_info->last_seen_msec = nm_utils_get_monotonic_timestamp_msec_cached (&now_msec) + - (((gint64) v_u32) * 1000); + } else if (initial) { + /* Unknown Age. Assume we just received it. */ + bss_info->last_seen_msec = nm_utils_get_monotonic_timestamp_msec_cached (&now_msec); + } + + p_ap_flags = bss_info->ap_flags; + if (nm_g_variant_lookup (properties, "Privacy", "b", &v_b)) + p_ap_flags = NM_FLAGS_ASSIGN (p_ap_flags, NM_802_11_AP_FLAGS_PRIVACY, v_b); + else { + nm_assert ( !initial + || !NM_FLAGS_HAS (p_ap_flags, NM_802_11_AP_FLAGS_PRIVACY)); + } + v_v = nm_g_variant_lookup_value (properties, "WPS", G_VARIANT_TYPE_VARDICT); + if ( v_v + || initial) { + NM80211ApFlags f = NM_802_11_AP_FLAGS_NONE; + + if (v_v) { + if (g_variant_lookup (v_v, "Type", "&s", &v_s)) { + p_ap_flags = NM_802_11_AP_FLAGS_WPS; + if (nm_streq (v_s, "pcb")) + f |= NM_802_11_AP_FLAGS_WPS_PBC; + else if (nm_streq (v_s, "pin")) + f |= NM_802_11_AP_FLAGS_WPS_PIN; + } + g_variant_unref (v_v); + } + p_ap_flags = NM_FLAGS_ASSIGN_MASK (p_ap_flags, + NM_802_11_AP_FLAGS_WPS + | NM_802_11_AP_FLAGS_WPS_PBC + | NM_802_11_AP_FLAGS_WPS_PIN, + f); + } + if (bss_info->ap_flags != p_ap_flags) { + bss_info->ap_flags = p_ap_flags; + nm_assert (bss_info->ap_flags == p_ap_flags); + } + + if (nm_g_variant_lookup (properties, "Mode", "&s", &v_s)) { + if (nm_streq (v_s, "infrastructure")) + p_mode = NM_802_11_MODE_INFRA; + else if (nm_streq (v_s, "ad-hoc")) + p_mode = NM_802_11_MODE_ADHOC; + else if (nm_streq (v_s, "mesh")) + p_mode = NM_802_11_MODE_MESH; + else + p_mode = NM_802_11_MODE_UNKNOWN; + } else if (initial) + p_mode = NM_802_11_MODE_UNKNOWN; + else + p_mode = bss_info->mode; + if (bss_info->mode != p_mode) { + bss_info->mode = p_mode; + nm_assert (bss_info->mode == p_mode); + } - iter = properties = g_dbus_proxy_get_cached_property_names (proxy); + if (nm_g_variant_lookup (properties, "Signal", "n", &v_i16)) + p_signal_percent = nm_wifi_utils_level_to_quality (v_i16); + else if (initial) + p_signal_percent = 0; + else + p_signal_percent = bss_info->signal_percent; + bss_info->signal_percent = p_signal_percent; - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - if (iter) { - while (*iter) { - GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter); + if (nm_g_variant_lookup (properties, "Frequency", "q", &v_u16)) + bss_info->frequency = v_u16; + + v_v = nm_g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); + if (v_v) { + arr_data = g_variant_get_fixed_array (v_v, &arr_len, 1); + arr_len = MIN (32, arr_len); + + /* Stupid ieee80211 layer uses <hidden> */ + if ( arr_data + && arr_len + && !( NM_IN_SET (arr_len, 8, 9) + && memcmp (arr_data, "<hidden>", arr_len) == 0) + && !nm_utils_is_empty_ssid (arr_data, arr_len)) { + /* good */ + } else + arr_len = 0; - g_variant_builder_add (&builder, "{sv}", *iter++, copy); - g_variant_unref (copy); + if (!nm_utils_gbytes_equal_mem (bss_info->ssid, arr_data, arr_len)) { + _nm_unused gs_unref_bytes GBytes *old_free = g_steal_pointer (&bss_info->ssid); + + bss_info->ssid = (arr_len == 0) + ? NULL + : g_bytes_new (arr_data, arr_len); + } + + g_variant_unref (v_v); + } else { + nm_assert ( !initial + || !bss_info->ssid); + } + + v_v = nm_g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); + if (v_v) { + arr_data = g_variant_get_fixed_array (v_v, &arr_len, 1); + if ( arr_len == ETH_ALEN + && memcmp (arr_data, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (arr_data, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) { + /* pass */ + } else + arr_len = 0; + + if (arr_len != 0) { + nm_assert (arr_len == sizeof (bss_info->bssid)); + bss_info->bssid_valid = TRUE; + memcpy (bss_info->bssid, arr_data, sizeof (bss_info->bssid)); + } else if (bss_info->bssid_valid) { + bss_info->bssid_valid = FALSE; + memset (bss_info->bssid, 0, sizeof (bss_info->bssid)); } + g_variant_unref (v_v); + } else { + nm_assert ( !initial + || !bss_info->bssid_valid); } - return g_variant_builder_end (&builder); + nm_assert ( ( bss_info->bssid_valid + && !nm_utils_memeqzero (bss_info->bssid, sizeof (bss_info->bssid))) + || ( !bss_info->bssid_valid + && nm_utils_memeqzero (bss_info->bssid, sizeof (bss_info->bssid)))); + + p_max_rate_has = FALSE; + p_max_rate = 0; + v_v = nm_g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au")); + if (v_v) { + const guint32 *rates = g_variant_get_fixed_array (v_v, &arr_len, sizeof (guint32)); + gsize i; + + for (i = 0; i < arr_len; i++) + p_max_rate = NM_MAX (p_max_rate, rates[i]); + p_max_rate_has = TRUE; + g_variant_unref (v_v); + } + v_v = nm_g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); + if (v_v) { + guint32 rate; + + arr_data = g_variant_get_fixed_array (v_v, &arr_len, 1); + nm_wifi_utils_parse_ies (arr_data, arr_len, &rate, &p_metered); + p_max_rate = NM_MAX (p_max_rate, rate); + p_max_rate_has = TRUE; + g_variant_unref (v_v); + + bss_info->metered = p_metered; + } + if (p_max_rate_has) + bss_info->max_rate = p_max_rate / 1000u; + + + v_v = nm_g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT); + if (v_v) { + bss_info->wpa_flags = security_from_vardict (v_v); + g_variant_unref (v_v); + } + + v_v = nm_g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT); + if (v_v) { + bss_info->rsn_flags = security_from_vardict (v_v); + g_variant_unref (v_v); + } + + _bss_info_changed_emit (self, bss_info, TRUE); } static void -bss_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +_bss_info_get_all_cb (GVariant *result, + GError *error, + gpointer user_data) { + NMSupplicantBssInfo *bss_info; NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - gs_free_error GError *error = NULL; - GVariant *props = NULL; - const char *object_path; - BssData *bss_data; - gboolean success; + gs_unref_variant GVariant *properties = NULL; - success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); if (nm_utils_error_is_cancelled (error)) return; - self = NM_SUPPLICANT_INTERFACE (user_data); + bss_info = user_data; + self = bss_info->_self; priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (!success) { - _LOGD ("failed to acquire BSS proxy: (%s)", error->message); - g_hash_table_remove (priv->bss_proxies, - g_dbus_proxy_get_object_path (proxy)); - return; - } + g_clear_object (&bss_info->_init_cancellable); + nm_c_list_move_tail (&priv->bss_lst_head, &bss_info->_bss_lst); - object_path = g_dbus_proxy_get_object_path (proxy); - bss_data = g_hash_table_lookup (priv->bss_proxies, object_path); - if (!bss_data) - return; - - bss_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (bss_proxy_properties_changed_cb), self); + if (result) + g_variant_get (result, "(@a{sv})", &properties); - props = bss_proxy_get_properties (self, proxy); - g_signal_emit (self, signals[BSS_UPDATED], 0, - g_dbus_proxy_get_object_path (proxy), - g_variant_ref_sink (props)); - g_variant_unref (props); + _bss_info_properties_changed (self, bss_info, properties, TRUE); - if (priv->scan_done_pending) - scan_done_emit_signal (self); + _starting_check_ready (self); } static void -bss_add_new (NMSupplicantInterface *self, const char *object_path) +_bss_info_add (NMSupplicantInterface *self, const char *object_path) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GDBusProxy *bss_proxy; - BssData *bss_data; + nm_auto_ref_string NMRefString *bss_path = NULL; + NMSupplicantBssInfo *bss_info; - g_return_if_fail (object_path != NULL); + bss_path = nm_ref_string_new (nm_dbus_path_not_empty (object_path)); + if (!bss_path) + return; - if (g_hash_table_lookup (priv->bss_proxies, object_path)) + bss_info = g_hash_table_lookup (priv->bss_idx, &bss_path); + if (bss_info) { + bss_info->_bss_dirty = FALSE; 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", NM_WPAS_DBUS_SERVICE, - "g-object-path", object_path, - "g-interface-name", NM_WPAS_DBUS_IFACE_BSS, - NULL); - bss_data = g_slice_new0 (BssData); - bss_data->proxy = bss_proxy; - g_hash_table_insert (priv->bss_proxies, - (char *) g_dbus_proxy_get_object_path (bss_proxy), - bss_data); - g_async_initable_init_async (G_ASYNC_INITABLE (bss_proxy), - G_PRIORITY_DEFAULT, - priv->other_cancellable, - (GAsyncReadyCallback) bss_proxy_acquired_cb, - self); + bss_info = g_slice_new (NMSupplicantBssInfo); + *bss_info = (NMSupplicantBssInfo) { + ._self = self, + .bss_path = g_steal_pointer (&bss_path), + ._init_cancellable = g_cancellable_new (), + }; + c_list_link_tail (&priv->bss_initializing_lst_head, &bss_info->_bss_lst); + g_hash_table_add (priv->bss_idx, bss_info); + + nm_dbus_connection_call_get_all (priv->dbus_connection, + priv->name_owner->str, + bss_info->bss_path->str, + NM_WPAS_DBUS_IFACE_BSS, + 5000, + bss_info->_init_cancellable, + _bss_info_get_all_cb, + bss_info); } -static void -peer_data_destroy (gpointer user_data) +static gboolean +_bss_info_remove (NMSupplicantInterface *self, + NMRefString **p_bss_path) { - PeerData *peer_data = user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantBssInfo *bss_info; + gpointer unused_but_required; + + if (!g_hash_table_steal_extended (priv->bss_idx, + p_bss_path, + (gpointer *) &bss_info, + &unused_but_required)) + return FALSE; + + c_list_unlink (&bss_info->_bss_lst); + if (!bss_info->_init_cancellable) + _bss_info_changed_emit (self, bss_info, FALSE); + _bss_info_destroy (bss_info); + + nm_assert_starting_has_pending_count (priv->starting_pending_count); - nm_clear_g_signal_handler (peer_data->proxy, &peer_data->change_id); - g_object_unref (peer_data->proxy); - g_slice_free (PeerData, peer_data); + return TRUE; } +/*****************************************************************************/ + static void -peer_proxy_properties_changed_cb (GDBusProxy *proxy, - GVariant *changed_properties, - char **invalidated_properties, - gpointer user_data) +_peer_info_destroy (NMSupplicantPeerInfo *peer_info) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + c_list_unlink (&peer_info->_peer_lst); + nm_clear_g_cancellable (&peer_info->_init_cancellable); - g_signal_emit (self, signals[PEER_UPDATED], 0, - g_dbus_proxy_get_object_path (proxy), - changed_properties); + g_free (peer_info->device_name); + g_free (peer_info->manufacturer); + g_free (peer_info->model); + g_free (peer_info->model_number); + g_free (peer_info->serial); + g_bytes_unref (peer_info->ies); + + nm_g_slice_free (peer_info); } -static GVariant * -peer_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy) +static void +_peer_info_changed_emit (NMSupplicantInterface *self, + NMSupplicantPeerInfo *peer_info, + gboolean is_present) { - gs_strfreev char **properties = NULL; - GVariantBuilder builder; - char **iter; + g_signal_emit (self, + signals[PEER_CHANGED], + 0, + peer_info, + is_present); +} + +static void +_peer_info_properties_changed (NMSupplicantInterface *self, + NMSupplicantPeerInfo *peer_info, + GVariant *properties, + gboolean initial) +{ + GVariant *v_v; + const char *v_s; + gint32 v_i32; + const guint8 *arr_data; + gsize arr_len; + + peer_info->last_seen_msec = nm_utils_get_monotonic_timestamp_msec (); - iter = properties = g_dbus_proxy_get_cached_property_names (proxy); + if (nm_g_variant_lookup (properties, "level", "i", &v_i32)) + peer_info->signal_percent = nm_wifi_utils_level_to_quality (v_i32); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - if (iter) { - while (*iter) { - GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter); + if (nm_g_variant_lookup (properties, "DeviceName", "&s", &v_s)) + nm_utils_strdup_reset (&peer_info->device_name, v_s); - g_variant_builder_add (&builder, "{sv}", *iter++, copy); - g_variant_unref (copy); + if (nm_g_variant_lookup (properties, "Manufacturer", "&s", &v_s)) + nm_utils_strdup_reset (&peer_info->manufacturer, v_s); + + if (nm_g_variant_lookup (properties, "Model", "&s", &v_s)) + nm_utils_strdup_reset (&peer_info->model, v_s); + + if (nm_g_variant_lookup (properties, "ModelNumber", "&s", &v_s)) + nm_utils_strdup_reset (&peer_info->model_number, v_s); + + if (nm_g_variant_lookup (properties, "Serial", "&s", &v_s)) + nm_utils_strdup_reset (&peer_info->serial, v_s); + + v_v = nm_g_variant_lookup_value (properties, "DeviceAddress", G_VARIANT_TYPE_BYTESTRING); + if (v_v) { + arr_data = g_variant_get_fixed_array (v_v, &arr_len, 1); + if ( arr_len == ETH_ALEN + && memcmp (arr_data, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (arr_data, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) { + /* pass */ + } else + arr_len = 0; + + if (arr_len != 0) { + nm_assert (arr_len == sizeof (peer_info->address)); + peer_info->address_valid = TRUE; + memcpy (peer_info->address, arr_data, sizeof (peer_info->address)); + } else if (peer_info->address_valid) { + peer_info->address_valid = FALSE; + memset (peer_info->address, 0, sizeof (peer_info->address)); } + g_variant_unref (v_v); + } else { + nm_assert ( !initial + || !peer_info->address_valid); } - return g_variant_builder_end (&builder); + nm_assert ( ( peer_info->address_valid + && !nm_utils_memeqzero (peer_info->address, sizeof (peer_info->address))) + || ( !peer_info->address_valid + && nm_utils_memeqzero (peer_info->address, sizeof (peer_info->address)))); + + /* The IEs property contains the WFD R1 subelements */ + v_v = nm_g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); + if (v_v) { + arr_data = g_variant_get_fixed_array (v_v, &arr_len, 1); + if (!nm_utils_gbytes_equal_mem (peer_info->ies, arr_data, arr_len)) { + _nm_unused gs_unref_bytes GBytes *old_free = g_steal_pointer (&peer_info->ies); + + peer_info->ies = g_bytes_new (arr_data, arr_len); + } else if ( arr_len == 0 + && !peer_info->ies) + peer_info->ies = g_bytes_new (NULL, 0); + g_variant_unref (v_v); + } + + _peer_info_changed_emit (self, peer_info, TRUE); } static void -peer_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +_peer_info_get_all_cb (GVariant *result, + GError *error, + gpointer user_data) { + NMSupplicantPeerInfo *peer_info; NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - gs_free_error GError *error = NULL; - GVariant *props = NULL; - const char *object_path; - PeerData *peer_data; - gboolean success; + gs_unref_variant GVariant *properties = NULL; - success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); if (nm_utils_error_is_cancelled (error)) return; - self = NM_SUPPLICANT_INTERFACE (user_data); + peer_info = user_data; + self = peer_info->_self; priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (!success) { - _LOGD ("failed to acquire Peer proxy: (%s)", error->message); - g_hash_table_remove (priv->peer_proxies, - g_dbus_proxy_get_object_path (proxy)); - return; - } - - object_path = g_dbus_proxy_get_object_path (proxy); - peer_data = g_hash_table_lookup (priv->peer_proxies, object_path); - if (!peer_data) - return; + g_clear_object (&peer_info->_init_cancellable); + nm_c_list_move_tail (&priv->peer_lst_head, &peer_info->_peer_lst); - peer_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (peer_proxy_properties_changed_cb), self); + if (result) + g_variant_get (result, "(@a{sv})", &properties); - props = peer_proxy_get_properties (self, proxy); + _peer_info_properties_changed (self, peer_info, properties, TRUE); - g_signal_emit (self, signals[PEER_UPDATED], 0, - g_dbus_proxy_get_object_path (proxy), - g_variant_ref_sink (props)); - g_variant_unref (props); + _starting_check_ready (self); } static void -peer_add_new (NMSupplicantInterface *self, const char *object_path) +_peer_info_add (NMSupplicantInterface *self, const char *object_path) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GDBusProxy *peer_proxy; - PeerData *peer_data; + nm_auto_ref_string NMRefString *peer_path = NULL; + NMSupplicantPeerInfo *peer_info; + + peer_path = nm_ref_string_new (nm_dbus_path_not_empty (object_path)); + if (!peer_path) + return; - g_return_if_fail (object_path != NULL); + peer_info = g_hash_table_lookup (priv->peer_idx, &peer_path); - if (g_hash_table_lookup (priv->peer_proxies, object_path)) + if (peer_info) { + peer_info->_peer_dirty = FALSE; return; + } - peer_proxy = g_object_new (G_TYPE_DBUS_PROXY, - "g-bus-type", G_BUS_TYPE_SYSTEM, - "g-flags", G_DBUS_PROXY_FLAGS_NONE, - "g-name", NM_WPAS_DBUS_SERVICE, - "g-object-path", object_path, - "g-interface-name", NM_WPAS_DBUS_IFACE_PEER, - NULL); - peer_data = g_slice_new0 (PeerData); - peer_data->proxy = peer_proxy; - g_hash_table_insert (priv->peer_proxies, - (char *) g_dbus_proxy_get_object_path (peer_proxy), - peer_data); - g_async_initable_init_async (G_ASYNC_INITABLE (peer_proxy), - G_PRIORITY_DEFAULT, - priv->other_cancellable, - (GAsyncReadyCallback) peer_proxy_acquired_cb, - self); + peer_info = g_slice_new (NMSupplicantPeerInfo); + *peer_info = (NMSupplicantPeerInfo) { + ._self = self, + .peer_path = g_steal_pointer (&peer_path), + ._init_cancellable = g_cancellable_new (), + }; + c_list_link_tail (&priv->peer_initializing_lst_head, &peer_info->_peer_lst); + g_hash_table_add (priv->peer_idx, peer_info); + + nm_dbus_connection_call_get_all (priv->dbus_connection, + priv->name_owner->str, + peer_info->peer_path->str, + NM_WPAS_DBUS_IFACE_PEER, + 5000, + peer_info->_init_cancellable, + _peer_info_get_all_cb, + peer_info); +} + +static gboolean +_peer_info_remove (NMSupplicantInterface *self, + NMRefString **p_peer_path) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantPeerInfo *peer_info; + gpointer unused_but_required; + + if (!g_hash_table_steal_extended (priv->peer_idx, + p_peer_path, + (gpointer *) &peer_info, + &unused_but_required)) + return FALSE; + + c_list_unlink (&peer_info->_peer_lst); + if (!peer_info->_init_cancellable) + _peer_info_changed_emit (self, peer_info, FALSE); + _peer_info_destroy (peer_info); + + nm_assert_starting_has_pending_count (priv->starting_pending_count); + + return TRUE; } /*****************************************************************************/ static void -set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state) +set_state_down (NMSupplicantInterface *self, + gboolean force_remove_from_supplicant, + const char *reason) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - NMSupplicantInterfaceState old_state = priv->state; + NMSupplicantBssInfo *bss_info; + NMSupplicantPeerInfo *peer_info; + NMSupplicantInterfaceState old_state; - if (new_state == priv->state) - return; + nm_assert (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DOWN); + nm_assert (!c_list_is_empty (&self->supp_lst)); + + _LOGD ("remove interface \"%s\" on %s (%s)%s", + priv->object_path->str, + priv->name_owner->str, + reason, + force_remove_from_supplicant ? " (remove in wpa_supplicant)" : ""); - /* DOWN is a terminal state */ - g_return_if_fail (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DOWN); + old_state = priv->state; - /* 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); + priv->state = NM_SUPPLICANT_INTERFACE_STATE_DOWN; - if (new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { - nm_clear_g_cancellable (&priv->other_cancellable); - priv->other_cancellable = g_cancellable_new (); - } else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { - nm_clear_g_cancellable (&priv->init_cancellable); - nm_clear_g_cancellable (&priv->other_cancellable); + _nm_supplicant_manager_unregister_interface (priv->supplicant_manager, self); - if (priv->iface_proxy) - g_signal_handlers_disconnect_by_data (priv->iface_proxy, self); + nm_assert (c_list_is_empty (&self->supp_lst)); + + if (force_remove_from_supplicant) { + _nm_supplicant_manager_dbus_call_remove_interface (priv->supplicant_manager, + priv->name_owner->str, + priv->object_path->str); } - priv->state = new_state; + _emit_signal_state (self, priv->state, old_state, 0); + + nm_clear_g_dbus_connection_signal (priv->dbus_connection, &priv->properties_changed_id); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, &priv->signal_id); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, &priv->bss_properties_changed_id); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, &priv->peer_properties_changed_id); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, &priv->p2p_group_properties_changed_id); - if ( priv->state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING - || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) - priv->last_scan = nm_utils_get_monotonic_timestamp_msec (); + nm_supplicant_interface_cancel_wps (self); - /* Disconnect reason is no longer relevant when not in the DISCONNECTED state */ - if (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED) - priv->disconnect_reason = 0; + if (priv->assoc_data) { + gs_free_error GError *error = NULL; - g_signal_emit (self, signals[STATE], 0, - (int) priv->state, - (int) old_state, - (int) priv->disconnect_reason); -} + nm_utils_error_set_cancelled (&error, TRUE, "NMSupplicantInterface"); + assoc_return (self, error, "cancelled because supplicant interface is going down"); + } -static void -set_state_from_string (NMSupplicantInterface *self, const char *new_state) -{ - NMSupplicantInterfaceState state; + while ((bss_info = c_list_first_entry (&priv->bss_initializing_lst_head, NMSupplicantBssInfo, _bss_lst))) { + g_hash_table_remove (priv->bss_idx, bss_info); + _bss_info_destroy (bss_info); + } + while ((bss_info = c_list_first_entry (&priv->bss_lst_head, NMSupplicantBssInfo, _bss_lst))) { + g_hash_table_remove (priv->bss_idx, bss_info); + _bss_info_destroy (bss_info); + } + nm_assert (g_hash_table_size (priv->bss_idx) == 0); - state = wpas_state_string_to_enum (new_state); - if (state == NM_SUPPLICANT_INTERFACE_STATE_INVALID) { - _LOGW ("unknown supplicant state '%s'", new_state); - return; + while ((peer_info = c_list_first_entry (&priv->peer_initializing_lst_head, NMSupplicantPeerInfo, _peer_lst))) { + g_hash_table_remove (priv->peer_idx, peer_info); + _peer_info_destroy (peer_info); } - set_state (self, state); + while ((peer_info = c_list_first_entry (&priv->peer_lst_head, NMSupplicantPeerInfo, _peer_lst))) { + g_hash_table_remove (priv->peer_idx, peer_info); + _peer_info_destroy (peer_info); + } + nm_assert (g_hash_table_size (priv->peer_idx) == 0); + + nm_clear_g_cancellable (&priv->main_cancellable); + nm_clear_g_cancellable (&priv->p2p_group_properties_cancellable); + + nm_clear_pointer (&priv->p2p_group_path, nm_ref_string_unref); + + _remove_network (self); + + nm_clear_pointer (&priv->current_bss, nm_ref_string_unref); } static void -set_scanning (NMSupplicantInterface *self, gboolean new_scanning) +set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInterfaceState old_state = priv->state; - if (priv->scanning != new_scanning) { - priv->scanning = new_scanning; + nm_assert (new_state > NM_SUPPLICANT_INTERFACE_STATE_STARTING); + nm_assert (new_state < NM_SUPPLICANT_INTERFACE_STATE_DOWN); + nm_assert (NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (new_state)); - /* Cache time of last scan completion */ - if (priv->scanning == FALSE) - priv->last_scan = nm_utils_get_monotonic_timestamp_msec (); + nm_assert (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_STARTING); + nm_assert (priv->state < NM_SUPPLICANT_INTERFACE_STATE_DOWN); - _notify (self, PROP_SCANNING); - } -} + if (new_state == priv->state) + return; -gboolean -nm_supplicant_interface_get_scanning (NMSupplicantInterface *self) -{ - NMSupplicantInterfacePrivate *priv; + _LOGT ("set state \"%s\" (was \"%s\")", + nm_supplicant_interface_state_to_string (new_state), + nm_supplicant_interface_state_to_string (priv->state)); - g_return_val_if_fail (self, FALSE); + if (old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) + priv->last_scan_msec = nm_utils_get_monotonic_timestamp_msec (); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (priv->scanning) - return TRUE; - if (priv->state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) - return TRUE; - return FALSE; + priv->state = new_state; + + _emit_signal_state (self, + priv->state, + old_state, + priv->state != NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED + ? 0u + : priv->disconnect_reason); } -const char * +NMRefString * 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; + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->current_bss; +} + +static inline gboolean +_prop_scanning_get (NMSupplicantInterfacePrivate *priv) +{ + return ( priv->scanning + || priv->supp_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) + && NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (priv->state); +} + +gboolean +nm_supplicant_interface_get_scanning (NMSupplicantInterface *self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); + + return _prop_scanning_get (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)); } gint64 nm_supplicant_interface_get_last_scan (NMSupplicantInterface *self) { - return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->last_scan; + NMSupplicantInterfacePrivate *priv; + + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* returns -1 if we are currently scanning. */ + return _prop_scanning_get (priv) + ? -1 + : priv->last_scan_msec; } #define MATCH_PROPERTY(p, n, v, t) (!strcmp (p, n) && g_variant_is_of_type (v, t)) @@ -556,12 +1102,11 @@ parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); gboolean have_active = FALSE; gboolean have_ssid = FALSE; - gboolean have_p2p = FALSE; gboolean have_ft = FALSE; gint32 max_scan_ssids = -1; const char **array; - g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT)); + nm_assert (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT)); if (g_variant_lookup (capabilities, "KeyMgmt", "^a&s", &array)) { have_ft = g_strv_contains (array, "wpa-ft-psk"); @@ -575,16 +1120,13 @@ parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) : NM_TERNARY_FALSE); if (g_variant_lookup (capabilities, "Modes", "^a&s", &array)) { - if (g_strv_contains (array, "p2p")) - have_p2p = TRUE; + /* Setting p2p_capable might toggle _prop_p2p_available_get(). However, + * we don't need to check for a property changed notification, because + * the caller did g_object_freeze_notify() and will perform the check. */ + priv->p2p_capable = g_strv_contains (array, "p2p"); g_free (array); } - if (priv->p2p_capable != have_p2p) { - priv->p2p_capable = have_p2p; - _notify (self, PROP_P2P_AVAILABLE); - } - if (g_variant_lookup (capabilities, "Scan", "^a&s", &array)) { if (g_strv_contains (array, "active")) have_active = TRUE; @@ -601,63 +1143,40 @@ parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) * 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); + _LOGD ("supports %d scan SSIDs", priv->max_scan_ssids); } } } static void -iface_check_ready (NMSupplicantInterface *self) +_starting_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); - } -} - -static void -iface_set_pmf_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - NMSupplicantInterface *self; - gs_unref_variant GVariant *variant = NULL; - gs_free_error GError *error = NULL; - - variant = g_dbus_proxy_call_finish (proxy, result, &error); - if (nm_utils_error_is_cancelled (error)) + if (priv->state != NM_SUPPLICANT_INTERFACE_STATE_STARTING) return; - self = NM_SUPPLICANT_INTERFACE (user_data); + if (priv->starting_pending_count > 0) + return; - if (error) - _LOGW ("failed to set Pmf=1: %s", error->message); + if (!c_list_is_empty (&priv->bss_initializing_lst_head)) + return; - iface_check_ready (self); -} + if (!c_list_is_empty (&priv->peer_initializing_lst_head)) + return; -gboolean -nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self) -{ - return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->group_proxy_acquired; -} + if (priv->p2p_group_properties_cancellable) + return; -const char* -nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self) -{ - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + nm_assert (priv->state == NM_SUPPLICANT_INTERFACE_STATE_STARTING); - if (priv->group_proxy_acquired) - return g_dbus_proxy_get_object_path (priv->group_proxy); - else - return NULL; -} + if (!NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (priv->supp_state)) { + _LOGW ("Supplicant state is unknown during initialization. Destroy the interface"); + set_state_down (self, TRUE, "failure to get valid interface state"); + return; + } -gboolean -nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self) -{ - return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_group_owner; + set_state (self, priv->supp_state); } static NMTernary @@ -739,179 +1258,328 @@ nm_supplicant_interface_get_auth_state (NMSupplicantInterface *self) /*****************************************************************************/ +static gboolean +_prop_p2p_group_joined_get (NMSupplicantInterfacePrivate *priv) +{ + return priv->p2p_group_path + && !priv->p2p_group_properties_cancellable; +} + +static gboolean +_prop_p2p_group_is_owner_get (NMSupplicantInterfacePrivate *priv) +{ + return _prop_p2p_group_joined_get (priv) + && priv->p2p_group_is_owner; +} + static void -_wps_data_free (WpsData *data) +_p2p_group_properties_changed (NMSupplicantInterface *self, + GVariant *properties) { - g_free (data->type); - g_free (data->pin); - g_free (data->bssid); - g_clear_object (&data->cancellable); - if (data->proxy && data->self) - g_signal_handlers_disconnect_by_data (data->proxy, data->self); - g_clear_object (&data->proxy); - g_slice_free (WpsData, data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + gboolean old_val_p2p_group_is_owner; + const char *s; + + old_val_p2p_group_is_owner = _prop_p2p_group_is_owner_get (priv); + + if (!properties) + priv->p2p_group_is_owner = FALSE; + else if (g_variant_lookup (properties, "Role", "&s", &s)) + priv->p2p_group_is_owner = nm_streq (s, "GO"); + + if (old_val_p2p_group_is_owner != _prop_p2p_group_is_owner_get (priv)) + _notify (self, PROP_P2P_GROUP_OWNER); } static void -_wps_credentials_changed_cb (GDBusProxy *proxy, - GVariant *props, - gpointer user_data) +_p2p_group_properties_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + gs_unref_variant GVariant *changed_properties = NULL; + + if (priv->p2p_group_properties_cancellable) + return; + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) + return; + + g_variant_get (parameters, + "(&s@a{sv}^a&s)", + NULL, + &changed_properties, + NULL); + + _p2p_group_properties_changed (self, changed_properties); +} + +static void +_p2p_group_properties_get_all_cb (GVariant *result, + GError *error, + gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gboolean old_val_p2p_group_joined; + gboolean old_val_p2p_group_is_owner; + gs_unref_variant GVariant *properties = NULL; + + if (nm_utils_error_is_cancelled (error)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + g_object_freeze_notify (G_OBJECT (self)); + + old_val_p2p_group_joined = _prop_p2p_group_joined_get (priv); + old_val_p2p_group_is_owner = _prop_p2p_group_is_owner_get (priv); + + nm_clear_g_cancellable (&priv->p2p_group_properties_cancellable); + + if (result) + g_variant_get (result, "(@a{sv})", &properties); + + _p2p_group_properties_changed (self, properties); + + _starting_check_ready (self); + + if (old_val_p2p_group_joined != _prop_p2p_group_joined_get (priv)) + _notify (self, PROP_P2P_GROUP_JOINED); + if (old_val_p2p_group_is_owner != _prop_p2p_group_is_owner_get (priv)) + _notify (self, PROP_P2P_GROUP_OWNER); + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +_p2p_group_set_path (NMSupplicantInterface *self, + const char *path) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + nm_auto_ref_string NMRefString *group_path = NULL; + gboolean old_val_p2p_group_joined; + gboolean old_val_p2p_group_is_owner; + + group_path = nm_ref_string_new (nm_dbus_path_not_empty (path)); + + if (priv->p2p_group_path == group_path) + return; + + old_val_p2p_group_joined = _prop_p2p_group_joined_get (priv); + old_val_p2p_group_is_owner = _prop_p2p_group_is_owner_get (priv); + + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->p2p_group_properties_changed_id); + nm_clear_g_cancellable (&priv->p2p_group_properties_cancellable); + + nm_ref_string_unref (priv->p2p_group_path); + priv->p2p_group_path = g_steal_pointer (&group_path); + + if (priv->p2p_group_path) { + priv->p2p_group_properties_cancellable = g_cancellable_new (); + priv->p2p_group_properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection, + priv->name_owner->str, + priv->p2p_group_path->str, + NM_WPAS_DBUS_IFACE_GROUP, + _p2p_group_properties_changed_cb, + self, + NULL); + nm_dbus_connection_call_get_all (priv->dbus_connection, + priv->name_owner->str, + priv->p2p_group_path->str, + NM_WPAS_DBUS_IFACE_GROUP, + 5000, + priv->p2p_group_properties_cancellable, + _p2p_group_properties_get_all_cb, + self); + } + + _notify (self, PROP_P2P_GROUP_PATH); + if (old_val_p2p_group_joined != _prop_p2p_group_joined_get (priv)) + _notify (self, PROP_P2P_GROUP_JOINED); + if (old_val_p2p_group_is_owner != _prop_p2p_group_is_owner_get (priv)) + _notify (self, PROP_P2P_GROUP_OWNER); + + nm_assert_starting_has_pending_count (priv->starting_pending_count); +} + +/*****************************************************************************/ + +static void +_wps_data_free (WpsData *wps_data, + GDBusConnection *dbus_connection) +{ + nm_clear_g_dbus_connection_signal (dbus_connection, + &wps_data->signal_id); + nm_clear_g_cancellable (&wps_data->cancellable); + g_free (wps_data->type); + g_free (wps_data->pin); + g_free (wps_data->bssid); + nm_g_slice_free (wps_data); +} + +static void +_wps_credentials_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMSupplicantInterface *self = user_data; + gs_unref_variant GVariant *props = NULL; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})"))) + return; + + g_variant_get (parameters, "(@a{sv})", &props); _LOGT ("wps: new credentials"); g_signal_emit (self, signals[WPS_CREDENTIALS], 0, props); } static void -_wps_handle_start_cb (GObject *source_object, - GAsyncResult *res, +_wps_handle_start_cb (GObject *source, + GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; - WpsData *data; - gs_unref_variant GVariant *result = NULL; + WpsData *wps_data; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; - result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (nm_utils_error_is_cancelled (error)) return; - data = user_data; - self = data->self; + wps_data = user_data; + self = wps_data->self; - if (result) + if (res) _LOGT ("wps: started with success"); else _LOGW ("wps: start failed with %s", error->message); - g_clear_object (&data->cancellable); - nm_clear_g_free (&data->type); - nm_clear_g_free (&data->pin); - nm_clear_g_free (&data->bssid); + g_clear_object (&wps_data->cancellable); + nm_clear_g_free (&wps_data->type); + nm_clear_g_free (&wps_data->pin); + nm_clear_g_free (&wps_data->bssid); } static void -_wps_handle_set_pc_cb (GObject *source_object, - GAsyncResult *res, +_wps_handle_set_pc_cb (GVariant *res, + GError *error, gpointer user_data) { - WpsData *data; NMSupplicantInterface *self; - gs_unref_variant GVariant *result = NULL; - gs_free_error GError *error = NULL; + NMSupplicantInterfacePrivate *priv; + WpsData *wps_data; GVariantBuilder start_args; guint8 bssid_buf[ETH_ALEN]; - result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (nm_utils_error_is_cancelled (error)) return; - data = user_data; - self = data->self; + wps_data = user_data; + self = wps_data->self; + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (result) + if (res) _LOGT ("wps: ProcessCredentials successfully set, starting..."); else _LOGW ("wps: ProcessCredentials failed to set (%s), starting...", error->message); - _nm_dbus_signal_connect (data->proxy, "Credentials", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (_wps_credentials_changed_cb), self); + wps_data->signal_id = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner->str, + NM_WPAS_DBUS_IFACE_INTERFACE_WPS, + "Credentials", + priv->object_path->str, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _wps_credentials_changed_cb, + self, + NULL); g_variant_builder_init (&start_args, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&start_args, "{sv}", "Role", g_variant_new_string ("enrollee")); - g_variant_builder_add (&start_args, "{sv}", "Type", g_variant_new_string (data->type)); - if (data->pin) - g_variant_builder_add (&start_args, "{sv}", "Pin", g_variant_new_string (data->pin)); - - if (data->bssid) { + g_variant_builder_add (&start_args, "{sv}", "Type", g_variant_new_string (wps_data->type)); + if (wps_data->pin) + g_variant_builder_add (&start_args, "{sv}", "Pin", g_variant_new_string (wps_data->pin)); + if (wps_data->bssid) { /* The BSSID is in fact not mandatory. If it is not set the supplicant would * enroll with any BSS in range. */ - if (!nm_utils_hwaddr_aton (data->bssid, bssid_buf, sizeof (bssid_buf))) + if (!nm_utils_hwaddr_aton (wps_data->bssid, bssid_buf, sizeof (bssid_buf))) nm_assert_not_reached (); g_variant_builder_add (&start_args, "{sv}", "Bssid", g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid_buf, ETH_ALEN, sizeof (guint8))); } - g_dbus_proxy_call (data->proxy, - "Start", - g_variant_new ("(a{sv})", &start_args), - G_DBUS_CALL_FLAGS_NONE, - -1, - data->cancellable, - _wps_handle_start_cb, - data); -} + wps_data->needs_cancelling = TRUE; + if (!wps_data->cancellable) + wps_data->cancellable = g_cancellable_new (); -static void -_wps_call_set_pc (WpsData *data) -{ - g_dbus_proxy_call (data->proxy, - "org.freedesktop.DBus.Properties.Set", - g_variant_new ("(ssv)", - NM_WPAS_DBUS_IFACE_INTERFACE_WPS, - "ProcessCredentials", - g_variant_new_boolean (TRUE)), - G_DBUS_CALL_FLAGS_NONE, - -1, - data->cancellable, - _wps_handle_set_pc_cb, - data); + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE_WPS, + "Start", + g_variant_new ("(a{sv})", &start_args), + G_VARIANT_TYPE ("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + 5000, + wps_data->cancellable, + _wps_handle_start_cb, + wps_data); } static void -_wps_handle_proxy_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +_wps_call_set_pc (NMSupplicantInterface *self, + WpsData *wps_data) { - NMSupplicantInterface *self; - NMSupplicantInterfacePrivate *priv; - WpsData *data; - gs_free_error GError *error = NULL; - GDBusProxy *proxy; - - proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - if (nm_utils_error_is_cancelled (error)) - return; - - data = user_data; - self = data->self; - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (!proxy) { - _LOGW ("wps: failure to create D-Bus proxy: %s", error->message); - _wps_data_free (data); - priv->wps_data = NULL; - return; - } + if (!wps_data->cancellable) + wps_data->cancellable = g_cancellable_new (); - data->proxy = proxy; - _LOGT ("wps: D-Bus proxy created. set ProcessCredentials..."); - _wps_call_set_pc (data); + nm_dbus_connection_call_set (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE_WPS, + "ProcessCredentials", + g_variant_new_boolean (TRUE), + 5000, + wps_data->cancellable, + _wps_handle_set_pc_cb, + wps_data); } static void -_wps_handle_cancel_cb (GObject *source_object, - GAsyncResult *res, +_wps_handle_cancel_cb (GObject *source, + GAsyncResult *result, gpointer user_data) { + GDBusConnection *dbus_connection = G_DBUS_CONNECTION (source); NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - WpsData *data; - gs_unref_variant GVariant *result = NULL; + WpsData *wps_data; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; - result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); - if (nm_utils_error_is_cancelled (error)) - return; + res = g_dbus_connection_call_finish (dbus_connection, result, &error); + nm_assert (!nm_utils_error_is_cancelled (error)); - data = user_data; - self = data->self; + wps_data = user_data; + self = wps_data->self; if (!self) { - _wps_data_free (data); - if (result) + _wps_data_free (wps_data, dbus_connection); + if (res) _LOGT ("wps: cancel completed successfully, after supplicant interface is gone"); else _LOGW ("wps: cancel failed (%s), after supplicant interface is gone", error->message); @@ -920,23 +1588,24 @@ _wps_handle_cancel_cb (GObject *source_object, priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - data->is_cancelling = FALSE; + wps_data->is_cancelling = FALSE; - if (!data->type) { + if (!wps_data->type) { priv->wps_data = NULL; - _wps_data_free (data); - if (result) + _wps_data_free (wps_data, dbus_connection); + if (res) _LOGT ("wps: cancel completed successfully"); else _LOGW ("wps: cancel failed (%s)", error->message); return; } - if (result) + if (res) _LOGT ("wps: cancel completed successfully, setting ProcessCredentials now..."); else _LOGW ("wps: cancel failed (%s), setting ProcessCredentials now...", error->message); - _wps_call_set_pc (data); + + _wps_call_set_pc (self, wps_data); } static void @@ -946,74 +1615,71 @@ _wps_start (NMSupplicantInterface *self, const char *pin) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - WpsData *data = priv->wps_data; + WpsData *wps_data; if (type) _LOGI ("wps: type %s start...", type); - if (!data) { + wps_data = priv->wps_data; + + if (!wps_data) { if (!type) return; - data = g_slice_new0 (WpsData); - data->self = self; - data->type = g_strdup (type); - data->bssid = g_strdup (bssid); - data->pin = g_strdup (pin); - data->cancellable = g_cancellable_new (); - - priv->wps_data = data; - - _LOGT ("wps: create D-Bus proxy..."); - - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - NM_WPAS_DBUS_SERVICE, - priv->object_path, - NM_WPAS_DBUS_IFACE_INTERFACE_WPS, - data->cancellable, - _wps_handle_proxy_cb, - data); - return; - } - - g_free (data->type); - g_free (data->bssid); - g_free (data->pin); - data->type = g_strdup (type); - data->bssid = g_strdup (bssid); - data->pin = g_strdup (pin); + if (priv->state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + _LOGD ("wps: interface is down. Cannot start with WPS"); + return; + } - if (!data->proxy) { - if (!type) { - nm_clear_g_cancellable (&data->cancellable); - priv->wps_data = NULL; - _wps_data_free (data); + wps_data = g_slice_new (WpsData); + *wps_data = (WpsData) { + .self = self, + .type = g_strdup (type), + .bssid = g_strdup (bssid), + .pin = g_strdup (pin), + }; + priv->wps_data = wps_data; + } else { + g_free (wps_data->type); + g_free (wps_data->bssid); + g_free (wps_data->pin); + wps_data->type = g_strdup (type); + wps_data->bssid = g_strdup (bssid); + wps_data->pin = g_strdup (pin); + } - _LOGT ("wps: abort creation of D-Bus proxy"); - } else - _LOGT ("wps: new enrollment. Wait for D-Bus proxy..."); + if (wps_data->is_cancelling) { + /* we wait for cancellation to complete. */ return; } - if (data->is_cancelling) + if ( !type + || wps_data->needs_cancelling) { + + _LOGT ("wps: cancel %senrollment...", + wps_data->needs_cancelling ? "previous " : ""); + + wps_data->is_cancelling = TRUE; + wps_data->needs_cancelling = FALSE; + nm_clear_g_cancellable (&wps_data->cancellable); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &wps_data->signal_id); + + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE_WPS, + "Cancel", + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + 5000, + NULL, + _wps_handle_cancel_cb, + wps_data); return; + } - _LOGT ("wps: cancel previous enrollment..."); - - data->is_cancelling = TRUE; - nm_clear_g_cancellable (&data->cancellable); - data->cancellable = g_cancellable_new (); - g_signal_handlers_disconnect_by_data (data->proxy, self); - g_dbus_proxy_call (data->proxy, - "Cancel", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - data->cancellable, - _wps_handle_cancel_cb, - data); + _LOGT ("wps: setting ProcessCredentials..."); + _wps_call_set_pc (self, wps_data); } void @@ -1034,875 +1700,202 @@ nm_supplicant_interface_cancel_wps (NMSupplicantInterface *self) /*****************************************************************************/ static void -iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +iface_introspect_cb (GObject *source, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - gs_unref_variant GVariant *variant = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; const char *data; NMTernary value; - variant = _nm_dbus_proxy_call_finish (proxy, result, - G_VARIANT_TYPE ("(s)"), - &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (nm_utils_error_is_cancelled (error)) return; self = NM_SUPPLICANT_INTERFACE (user_data); priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (NM_SUPPL_CAP_MASK_GET (priv->global_capabilities, NM_SUPPL_CAP_TYPE_AP) == NM_TERNARY_DEFAULT) { - /* if the global value is set, we trust it and ignore whatever we get from introspection. */ - } else { - value = NM_TERNARY_DEFAULT; - if (variant) { - g_variant_get (variant, "(&s)", &data); - - /* The ProbeRequest method only exists if AP mode has been enabled */ - value = strstr (data, "ProbeRequest") - ? NM_TERNARY_TRUE - : NM_TERNARY_FALSE; - } - priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET (priv->iface_capabilities, NM_SUPPL_CAP_TYPE_AP, value); - } + nm_assert (NM_SUPPL_CAP_MASK_GET (priv->global_capabilities, NM_SUPPL_CAP_TYPE_AP) == NM_TERNARY_DEFAULT); - iface_check_ready (self); -} + value = NM_TERNARY_DEFAULT; + if (res) { + g_variant_get (res, "(&s)", &data); -static void -scan_done_emit_signal (NMSupplicantInterface *self) -{ - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - const char *object_path; - BssData *bss_data; - gboolean success; - GHashTableIter iter; - - g_hash_table_iter_init (&iter, priv->bss_proxies); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &bss_data)) { - /* we have some BSS' that need to be initialized first. Delay - * emitting signal. */ - if (!bss_data->change_id) { - priv->scan_done_pending = TRUE; - return; - } + /* The ProbeRequest method only exists if AP mode has been enabled */ + value = strstr (data, "ProbeRequest") + ? NM_TERNARY_TRUE + : NM_TERNARY_FALSE; } - /* Emit BSS_UPDATED 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 *) &object_path, (gpointer *) &bss_data)) { - gs_unref_variant GVariant *props = NULL; - - props = bss_proxy_get_properties (self, bss_data->proxy); - g_signal_emit (self, signals[BSS_UPDATED], 0, - object_path, - g_variant_ref_sink (props)); - } + priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET (priv->iface_capabilities, NM_SUPPL_CAP_TYPE_AP, value); - success = priv->scan_done_success; - priv->scan_done_success = FALSE; - priv->scan_done_pending = FALSE; - g_signal_emit (self, signals[SCAN_DONE], 0, success); + priv->starting_pending_count--; + _starting_check_ready (self); } static void -wpas_iface_scan_done (GDBusProxy *proxy, - gboolean success, - gpointer user_data) +_properties_changed_main (NMSupplicantInterface *self, + GVariant *properties) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - /* Cache last scan completed time */ - priv->last_scan = nm_utils_get_monotonic_timestamp_msec (); - priv->scan_done_success |= success; - scan_done_emit_signal (self); -} - -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_msec (); - - bss_add_new (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); - BssData *bss_data; - - bss_data = g_hash_table_lookup (priv->bss_proxies, path); - if (!bss_data) - return; - g_hash_table_steal (priv->bss_proxies, path); - g_signal_emit (self, signals[BSS_REMOVED], 0, path); - bss_data_destroy (bss_data); -} - -static void -eap_changed (GDBusProxy *proxy, - const char *status, - const char *parameter, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - NMSupplicantAuthState auth_state = NM_SUPPLICANT_AUTH_STATE_UNKNOWN; - - if (nm_streq0 (status, "started")) - auth_state = NM_SUPPLICANT_AUTH_STATE_STARTED; - else if (nm_streq0 (status, "completion")) { - if (nm_streq0 (parameter, "success")) - auth_state = NM_SUPPLICANT_AUTH_STATE_SUCCESS; - else if (nm_streq0 (parameter, "failure")) - auth_state = NM_SUPPLICANT_AUTH_STATE_FAILURE; - } - - /* the state eventually reaches one of started, success or failure - * so ignore any other intermediate (unknown) state change. */ - if ( auth_state != NM_SUPPLICANT_AUTH_STATE_UNKNOWN - && auth_state != priv->auth_state) { - priv->auth_state = auth_state; - _notify (self, PROP_AUTH_STATE); - } -} - -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) - bss_add_new (self, *iter++); - g_free (array); - } - - if (g_variant_lookup (changed_properties, "CurrentBSS", "&o", &s)) { - s = nm_dbus_path_not_empty (s); - if (!nm_streq0 (s, priv->current_bss)) { - g_free (priv->current_bss); - priv->current_bss = g_strdup (s); - _notify (self, PROP_CURRENT_BSS); + const char **v_strv; + const char *v_s; + gboolean v_b; + gint32 v_i32; + GVariant *v_v; + gboolean do_log_driver_info = FALSE; + gboolean do_set_state = FALSE; + gboolean do_notify_current_bss = FALSE; + + nm_assert (properties || g_variant_is_of_type (properties, G_VARIANT_TYPE ("a{sv}"))); + + v_v = g_variant_lookup_value (properties, "Capabilities", G_VARIANT_TYPE_VARDICT); + if (v_v) { + parse_capabilities (self, v_v); + g_variant_unref (v_v); + } + + if (nm_g_variant_lookup (properties, "Scanning", "b", &v_b)) { + if (priv->scanning != (!!v_b)) { + if (priv->scanning) + priv->last_scan_msec = nm_utils_get_monotonic_timestamp_msec (); + priv->scanning = v_b; } } - v = g_variant_lookup_value (changed_properties, "Capabilities", G_VARIANT_TYPE_VARDICT); - if (v) { - parse_capabilities (self, v); - g_variant_unref (v); + if (nm_g_variant_lookup (properties, "Ifname", "&s", &v_s)) { + if (nm_utils_strdup_reset (&priv->ifname, v_s)) + do_log_driver_info = TRUE; + } + if (nm_g_variant_lookup (properties, "Driver", "&s", &v_s)) { + if (nm_utils_strdup_reset (&priv->driver, v_s)) + do_log_driver_info = TRUE; } - if (g_variant_lookup (changed_properties, "DisconnectReason", "i", &i32)) { + if (nm_g_variant_lookup (properties, "DisconnectReason", "i", &v_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); - } - - /* We may not have priv->dev set yet if this interface was created from a - * known wpa_supplicant interface without knowing the device name. - */ - if (priv->dev == NULL && g_variant_lookup (changed_properties, "Ifname", "&s", &s)) { - priv->dev = g_strdup (s); - _notify (self, PROP_IFACE); - } - - g_object_thaw_notify (G_OBJECT (self)); -} - -static void -group_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); - char *s; - - g_object_freeze_notify (G_OBJECT (self)); - -#if 0 - v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); - if (v) { - bytes = g_variant_get_fixed_array (v, &len, 1); - if ( len == ETH_ALEN - && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 - && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) - nm_wifi_p2p_group_set_bssid_bin (group, bytes); - g_variant_unref (v); - } - - v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); - if (v) { - bytes = g_variant_get_fixed_array (v, &len, 1); - len = MIN (32, len); - - /* Stupid ieee80211 layer uses <hidden> */ - if ( bytes && len - && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8)) - && !nm_utils_is_empty_ssid (bytes, len)) - nm_wifi_p2p_group_set_ssid (group, bytes, len); - - g_variant_unref (v); - } -#endif - - if (g_variant_lookup (changed_properties, "Role", "s", &s)) { - priv->p2p_group_owner = g_strcmp0 (s, "GO") == 0; - _notify (self, PROP_P2P_GROUP_OWNER); - g_free (s); - } - - /* NOTE: We do not seem to get any property change notifications for the Members - * property. However, we can keep track of these indirectly either by querying - * the groups that each peer is in or listening to the Join/Disconnect - * notifications. - */ - - g_object_thaw_notify (G_OBJECT (self)); -} - -static void -group_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - NMSupplicantInterface *self; - NMSupplicantInterfacePrivate *priv; - gs_free_error GError *error = NULL; - gboolean success; - - success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); - if (nm_utils_error_is_cancelled (error)) - return; - - self = NM_SUPPLICANT_INTERFACE (user_data); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - if (!success) { - _LOGD ("failed to acquire Group proxy: (%s)", error->message); - g_clear_object (&priv->group_proxy); - return; - } - - priv->group_proxy_acquired = TRUE; - _notify (self, PROP_P2P_GROUP_JOINED); - _notify (self, PROP_P2P_GROUP_PATH); - - iface_check_ready (self); -} - -static void -p2p_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 **array, **iter; - const char *path = NULL; - - g_object_freeze_notify (G_OBJECT (self)); - - if (g_variant_lookup (changed_properties, "Peers", "^a&o", &array)) { - iter = array; - while (*iter) - peer_add_new (self, *iter++); - g_free (array); - } - - if (g_variant_lookup (changed_properties, "Group", "&o", &path)) { - if (priv->group_proxy && g_strcmp0 (path, g_dbus_proxy_get_object_path (priv->group_proxy)) == 0) { - /* We already have the proxy, nothing to do. */ - } else if (nm_dbus_path_not_empty (path)) { - if (priv->group_proxy != NULL) { - _LOGW ("P2P: Unexpected update of the group object path"); - priv->group_proxy_acquired = FALSE; - _notify (self, PROP_P2P_GROUP_JOINED); - _notify (self, PROP_P2P_GROUP_PATH); - g_clear_object (&priv->group_proxy); + priv->disconnect_reason = v_i32; + } + + if (nm_g_variant_lookup (properties, "State", "&s", &v_s)) { + NMSupplicantInterfaceState state; + + state = wpas_state_string_to_enum (v_s); + if (state == NM_SUPPLICANT_INTERFACE_STATE_INVALID) + _LOGT ("ignore unknown supplicant state '%s'", v_s); + else if (priv->supp_state != state) { + priv->supp_state = state; + if (priv->state > NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + /* Only transition to actual wpa_supplicant interface states (ie, + * anything > STARTING) after the NMSupplicantInterface has had a + * chance to initialize, which is signalled by entering the STARTING + * state. + */ + do_set_state = TRUE; } - - /* Delay ready state if we have not reached it yet. */ - if (priv->ready_count) - priv->ready_count++; - - priv->group_proxy = g_object_new (G_TYPE_DBUS_PROXY, - "g-bus-type", G_BUS_TYPE_SYSTEM, - "g-flags", G_DBUS_PROXY_FLAGS_NONE, - "g-name", NM_WPAS_DBUS_SERVICE, - "g-object-path", path, - "g-interface-name", NM_WPAS_DBUS_IFACE_GROUP, - NULL); - g_signal_connect (priv->group_proxy, "g-properties-changed", G_CALLBACK (group_props_changed_cb), self); - g_async_initable_init_async (G_ASYNC_INITABLE (priv->group_proxy), - G_PRIORITY_DEFAULT, - priv->other_cancellable, - (GAsyncReadyCallback) group_proxy_acquired_cb, - self); - } else { - priv->group_proxy_acquired = FALSE; - _notify (self, PROP_P2P_GROUP_JOINED); - _notify (self, PROP_P2P_GROUP_PATH); - g_clear_object (&priv->group_proxy); } } - g_object_thaw_notify (G_OBJECT (self)); -} - -static void -p2p_device_found (GDBusProxy *proxy, - const char *path, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - - peer_add_new (self, path); -} - -static void -p2p_device_lost (GDBusProxy *proxy, - const char *path, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - PeerData *peer_data; - - peer_data = g_hash_table_lookup (priv->peer_proxies, path); - if (!peer_data) - return; - g_hash_table_steal (priv->peer_proxies, path); - g_signal_emit (self, signals[PEER_REMOVED], 0, path); - peer_data_destroy (peer_data); -} - -static void -p2p_group_started (GDBusProxy *proxy, - GVariant *params, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - NMSupplicantInterface *iface = NULL; - char *group_path = NULL; - char *iface_path = NULL; - - /* There is one more parameter: the role, but we don't really care about that here. */ - if (!g_variant_lookup (params, "group_object", "&o", &group_path)) { - _LOGW ("P2P: GroupStarted signal is missing the \"group_object\" parameter"); - return; - } - - if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { - _LOGW ("P2P: GroupStarted signal is missing the \"interface\" parameter"); - return; - } - - if (g_strcmp0 (iface_path, priv->object_path) == 0) { - _LOGW ("P2P: GroupStarted on existing interface"); - iface = g_object_ref (self); - } else { - iface = nm_supplicant_manager_create_interface_from_path (nm_supplicant_manager_get (), - iface_path); - if (iface == NULL) { - _LOGW ("P2P: Group interface already exists in GroupStarted handler, aborting further processing."); - return; + if (nm_g_variant_lookup (properties, "CurrentBSS", "&o", &v_s)) { + v_s = nm_dbus_path_not_empty (v_s); + if (!nm_ref_string_equals_str (priv->current_bss, v_s)) { + nm_ref_string_unref (priv->current_bss); + priv->current_bss = nm_ref_string_new (v_s); + do_notify_current_bss = TRUE; } } - /* Signal existence of the (new) interface. */ - g_signal_emit (self, signals[GROUP_STARTED], 0, iface); - g_object_unref (iface); -} - -static void -p2p_group_finished (GDBusProxy *proxy, - GVariant *params, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - const char *iface_path = NULL; - /* TODO: Group finished is called on the management interface! - * This means the signal consumer will currently need to assume which - * interface is finishing or it needs to match the object paths. - */ - - if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { - _LOGW ("P2P: GroupFinished signal is missing the \"interface\" parameter"); - return; + if (do_log_driver_info) { + _LOGD ("supplicant interface for ifindex=%d, ifname=%s%s%s, driver=%s%s%s (requested %s)", + priv->ifindex, + NM_PRINT_FMT_QUOTE_STRING (priv->ifname), + NM_PRINT_FMT_QUOTE_STRING (priv->driver), + nm_supplicant_driver_to_string (priv->requested_driver)); } - _LOGD ("P2P: GroupFinished signal on interface %s for interface %s", priv->object_path, iface_path); + if (nm_g_variant_lookup (properties, "BSSs", "^a&o", &v_strv)) { + NMSupplicantBssInfo *bss_info; + NMSupplicantBssInfo *bss_info_safe; + const char **iter; - /* Signal group finish interface (on management interface). */ - g_signal_emit (self, signals[GROUP_FINISHED], 0, iface_path); -} + c_list_for_each_entry (bss_info, &priv->bss_lst_head, _bss_lst) + bss_info->_bss_dirty = TRUE; + c_list_for_each_entry (bss_info, &priv->bss_initializing_lst_head, _bss_lst) + bss_info->_bss_dirty = TRUE; -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 (!nm_utils_error_is_cancelled (error)) { - 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, "EAP", G_VARIANT_TYPE ("(ss)"), - G_CALLBACK (eap_changed), self); - - /* Scan result aging parameters */ - g_dbus_proxy_call (priv->iface_proxy, - DBUS_INTERFACE_PROPERTIES ".Set", - g_variant_new ("(ssv)", - NM_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, - DBUS_INTERFACE_PROPERTIES ".Set", - g_variant_new ("(ssv)", - NM_WPAS_DBUS_IFACE_INTERFACE, - "BSSExpireCount", - g_variant_new_uint32 (2)), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->init_cancellable, - NULL, - NULL); - - if (_get_capability (priv, NM_SUPPL_CAP_TYPE_PMF) == NM_TERNARY_TRUE) { - /* Initialize global PMF setting to 'optional' */ - priv->ready_count++; - g_dbus_proxy_call (priv->iface_proxy, - DBUS_INTERFACE_PROPERTIES ".Set", - g_variant_new ("(ssv)", - NM_WPAS_DBUS_IFACE_INTERFACE, - "Pmf", - g_variant_new_string ("1")), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->init_cancellable, - (GAsyncReadyCallback) iface_set_pmf_cb, - self); - } - - if (_get_capability (priv, NM_SUPPL_CAP_TYPE_AP) == NM_TERNARY_DEFAULT) { - /* 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); - } - - iface_check_ready (self); -} - -static void -on_p2p_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 (!nm_utils_error_is_cancelled (error)) { - self = NM_SUPPLICANT_INTERFACE (user_data); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - _LOGW ("failed to acquire wpa_supplicant p2p proxy: (%s)", error->message); + for (iter = v_strv; *iter; iter++) + _bss_info_add (self, *iter); - g_clear_object (&priv->p2p_proxy); + g_free (v_strv); - iface_check_ready (self); + c_list_for_each_entry_safe (bss_info, bss_info_safe, &priv->bss_initializing_lst_head, _bss_lst) { + if (bss_info->_bss_dirty) + _bss_info_remove (self, &bss_info->bss_path); } - return; - } - - self = NM_SUPPLICANT_INTERFACE (user_data); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceFound", G_VARIANT_TYPE ("(o)"), - G_CALLBACK (p2p_device_found), self); - _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceLost", G_VARIANT_TYPE ("(o)"), - G_CALLBACK (p2p_device_lost), self); - _nm_dbus_signal_connect (priv->p2p_proxy, "GroupStarted", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (p2p_group_started), self); - _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFinished", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (p2p_group_finished), self); - /* TODO: - * * WpsFailed - * * FindStopped - * * GONegotationFailure - * * InvitationReceived - */ - - priv->p2p_proxy_acquired = TRUE; - _notify (self, PROP_P2P_AVAILABLE); - - iface_check_ready (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->ready_count = 1; - - priv->object_path = g_strdup (path); - _notify (self, PROP_OBJECT_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", NM_WPAS_DBUS_SERVICE, - "g-object-path", priv->object_path, - "g-interface-name", NM_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); - - if (_get_capability (priv, NM_SUPPL_CAP_TYPE_P2P) == NM_TERNARY_TRUE) { - priv->ready_count++; - priv->p2p_proxy = g_object_new (G_TYPE_DBUS_PROXY, - "g-bus-type", G_BUS_TYPE_SYSTEM, - "g-flags", G_DBUS_PROXY_FLAGS_NONE, - "g-name", NM_WPAS_DBUS_SERVICE, - "g-object-path", priv->object_path, - "g-interface-name", NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, - NULL); - g_signal_connect (priv->p2p_proxy, "g-properties-changed", G_CALLBACK (p2p_props_changed_cb), self); - g_async_initable_init_async (G_ASYNC_INITABLE (priv->p2p_proxy), - G_PRIORITY_DEFAULT, - priv->init_cancellable, - (GAsyncReadyCallback) on_p2p_proxy_acquired, - self); - } -} - -static void -interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - NMSupplicantInterface *self; - 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 (nm_utils_error_is_cancelled (error)) - return; - - self = NM_SUPPLICANT_INTERFACE (user_data); - - 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 (nm_utils_error_is_cancelled (error)) - 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, NM_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); - } -} - -static void -interface_removed_cb (GDBusProxy *proxy, - const char *path, - gpointer user_data) -{ - NMSupplicantInterface *self; - NMSupplicantInterfacePrivate *priv; - - self = NM_SUPPLICANT_INTERFACE (user_data); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - if (g_strcmp0 (priv->object_path, path) != 0) - return; - - _LOGD ("Received interface removed signal"); - - /* The interface may lose its last reference during signal handling otherwise. */ - g_object_ref (self); - - /* Invalidate the object path to prevent the manager from trying to remove - * a non-existing interface. */ - g_clear_pointer (&priv->object_path, g_free); - _notify (self, PROP_OBJECT_PATH); - - /* No need to clean up everything now, that will happen at dispose time. */ - - /* Interface is down and has been removed. */ - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); - g_signal_emit (self, signals[REMOVED], 0); - - g_object_unref (self); -} - -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; - - wpas_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); - if (!wpas_proxy) { - if (!nm_utils_error_is_cancelled (error)) { - self = NM_SUPPLICANT_INTERFACE (user_data); - _LOGW ("failed to acquire wpa_supplicant proxy: (%s)", error->message); - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + c_list_for_each_entry_safe (bss_info, bss_info_safe, &priv->bss_lst_head, _bss_lst) { + if (bss_info->_bss_dirty) + _bss_info_remove (self, &bss_info->bss_path); } - return; } - self = NM_SUPPLICANT_INTERFACE (user_data); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + if (do_notify_current_bss) + _notify (self, PROP_CURRENT_BSS); - priv->wpas_proxy = wpas_proxy; - - /* Watch for interface removal. */ - _nm_dbus_signal_connect (priv->wpas_proxy, "InterfaceRemoved", G_VARIANT_TYPE ("(o)"), - G_CALLBACK (interface_removed_cb), self); - - /* 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. - */ - - if (priv->dev != NULL) { - const char *driver_name; - - driver_name = nm_supplicant_driver_to_string (priv->driver); - - 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); - } else if (priv->object_path) { - interface_add_done (self, priv->object_path); - } else { - g_assert_not_reached (); - } + if (do_set_state) + set_state (self, priv->supp_state); } static void -interface_add (NMSupplicantInterface *self) +_properties_changed_p2p_device (NMSupplicantInterface *self, + GVariant *properties) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char **v_strv; + const char *v_s; - /* Can only start the interface from INIT state */ - g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT); + nm_assert (!properties || g_variant_is_of_type (properties, G_VARIANT_TYPE ("a{sv}"))); - _LOGD ("adding interface to supplicant"); + if (nm_g_variant_lookup (properties, "Peers", "^a&o", &v_strv)) { + NMSupplicantPeerInfo *peer_info; + NMSupplicantPeerInfo *peer_info_safe; + const char *const*iter; - /* Move to starting to prevent double-calls of interface_add() */ - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING); + c_list_for_each_entry (peer_info, &priv->peer_lst_head, _peer_lst) + peer_info->_peer_dirty = TRUE; + c_list_for_each_entry (peer_info, &priv->peer_initializing_lst_head, _peer_lst) + peer_info->_peer_dirty = TRUE; - nm_clear_g_cancellable (&priv->init_cancellable); - priv->init_cancellable = g_cancellable_new (); + for (iter = v_strv; *iter; iter++) + _peer_info_add (self, *iter); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - NM_WPAS_DBUS_SERVICE, - NM_WPAS_DBUS_PATH, - NM_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); + g_free (v_strv); - 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); + c_list_for_each_entry_safe (peer_info, peer_info_safe, &priv->peer_initializing_lst_head, _peer_lst) { + if (peer_info->_peer_dirty) + _peer_info_remove (self, &peer_info->peer_path); + } + c_list_for_each_entry_safe (peer_info, peer_info_safe, &priv->peer_lst_head, _peer_lst) { + if (peer_info->_peer_dirty) + _peer_info_remove (self, &peer_info->peer_path); + } } -} -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 - && !nm_utils_error_is_cancelled (error) - && !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); - } + if (nm_g_variant_lookup (properties, "Group", "&o", &v_s)) + _p2p_group_set_path (self, v_s); } /*****************************************************************************/ static void -assoc_return (NMSupplicantInterface *self, GError *error, const char *message) +assoc_return (NMSupplicantInterface *self, + GError *error, + const char *message) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); AssocData *assoc_data; @@ -1913,9 +1906,14 @@ assoc_return (NMSupplicantInterface *self, GError *error, const char *message) if (error) { g_dbus_error_strip_remote_error (error); - _LOGW ("assoc[%p]: %s: %s", assoc_data, message, error->message); - } else - _LOGD ("assoc[%p]: association request successful", assoc_data); + _LOGW ("assoc["NM_HASH_OBFUSCATE_PTR_FMT"]: %s: %s", + NM_HASH_OBFUSCATE_PTR (assoc_data), + message, + error->message); + } else { + _LOGD ("assoc["NM_HASH_OBFUSCATE_PTR_FMT"]: association request successful", + NM_HASH_OBFUSCATE_PTR (assoc_data)); + } if (assoc_data->add_network_data) { /* signal that this request already completed */ @@ -1941,111 +1939,82 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - /* Cancel all pending calls related to a prior connection attempt */ - if (priv->assoc_data) { - gs_free_error GError *error = NULL; - - nm_utils_error_set_cancelled (&error, FALSE, "NMSupplicantInterface"); - assoc_return (self, error, "abort due to disconnect"); - } - - /* 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; + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "Disconnect", + NULL, + G_VARIANT_TYPE ("()"), + "disconnect"); } + _remove_network (self); + /* Cancel any WPS enrollment, if any */ nm_supplicant_interface_cancel_wps (self); + + /* Cancel all pending calls related to a prior connection attempt */ + if (priv->assoc_data) { + gs_free_error GError *error = NULL; + + nm_utils_error_set_cancelled (&error, FALSE, "NMSupplicantInterface"); + assoc_return (self, error, "abort due to disconnect"); + } } static void -disconnect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +disconnect_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - DisconnectData *disconnect_data = user_data; - gs_unref_object NMSupplicantInterface *self = disconnect_data->self; - gs_unref_variant GVariant *reply = NULL; + gs_unref_object NMSupplicantInterface *self = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; + NMSupplicantInterfaceDisconnectCb callback; + gpointer callback_user_data; + + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); - reply = g_dbus_proxy_call_finish (proxy, result, &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); - /* an already disconnected interface is not an error*/ - if ( !reply + if ( !res && !strstr (error->message, "fi.w1.wpa_supplicant1.NotConnected")) { + /* an already disconnected interface is not an error*/ g_clear_error(&error); } - disconnect_data->callback(self, error, disconnect_data->user_data); - g_slice_free (DisconnectData, disconnect_data); + callback (self, error, callback_user_data); } void -nm_supplicant_interface_disconnect_async ( NMSupplicantInterface * self, - GCancellable * cancellable, - NMSupplicantInterfaceDisconnectCb callback, - gpointer user_data) +nm_supplicant_interface_disconnect_async (NMSupplicantInterface *self, + GCancellable *cancellable, + NMSupplicantInterfaceDisconnectCb callback, + gpointer user_data) { - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DisconnectData *disconnect_data; - - /* Don't do anything if there is no connection to the supplicant yet. */ - if (!priv->iface_proxy) - return; - g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); - g_return_if_fail (NULL != callback); + g_return_if_fail (callback); - disconnect_data = g_slice_new0(DisconnectData); - - /* Keep interface alive until disconnect finishes */ - disconnect_data->self = g_object_ref (self); - disconnect_data->callback = callback; - disconnect_data->user_data = user_data; - - /* Disconnect the interface */ - g_dbus_proxy_call (priv->iface_proxy, - "Disconnect", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - (GAsyncReadyCallback) disconnect_cb, - disconnect_data); + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "Disconnect", + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + cancellable, + disconnect_cb, + nm_utils_user_data_pack (g_object_ref (self), callback, user_data)); } static void -assoc_select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +assoc_select_network_cb (GObject *source, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; - gs_unref_variant GVariant *reply = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; - reply = g_dbus_proxy_call_finish (proxy, result, &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (nm_utils_error_is_cancelled (error)) return; @@ -2061,25 +2030,27 @@ assoc_call_select_network (NMSupplicantInterface *self) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - g_dbus_proxy_call (priv->iface_proxy, - "SelectNetwork", - g_variant_new ("(o)", priv->net_path), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->assoc_data->cancellable, - (GAsyncReadyCallback) assoc_select_network_cb, - self); + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "SelectNetwork", + g_variant_new ("(o)", priv->net_path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + priv->assoc_data->cancellable, + assoc_select_network_cb, + self); } static void -assoc_add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +assoc_add_blob_cb (GObject *source, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - gs_unref_variant GVariant *reply = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; - reply = g_dbus_proxy_call_finish (proxy, result, &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (nm_utils_error_is_cancelled (error)) return; @@ -2092,52 +2063,62 @@ assoc_add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) } priv->assoc_data->blobs_left--; - _LOGT ("assoc[%p]: blob added (%u left)", priv->assoc_data, priv->assoc_data->blobs_left); + _LOGT ("assoc["NM_HASH_OBFUSCATE_PTR_FMT"]: blob added (%u left)", + NM_HASH_OBFUSCATE_PTR (priv->assoc_data), + priv->assoc_data->blobs_left); if (priv->assoc_data->blobs_left == 0) assoc_call_select_network (self); } static void -assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +assoc_add_network_cb (GObject *source, GAsyncResult *result, gpointer user_data) { AddNetworkData *add_network_data = user_data; AssocData *assoc_data; NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - gs_unref_variant GVariant *reply = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; GHashTable *blobs; GHashTableIter iter; const char *blob_name; GBytes *blob_data; + nm_auto_ref_string NMRefString *name_owner = NULL; + nm_auto_ref_string NMRefString *object_path = NULL; + + g_clear_object (&add_network_data->shutdown_wait_obj); assoc_data = add_network_data->assoc_data; if (assoc_data) assoc_data->add_network_data = NULL; - g_slice_free (AddNetworkData, add_network_data); + name_owner = g_steal_pointer (&add_network_data->name_owner); + object_path = g_steal_pointer (&add_network_data->object_path); + nm_g_slice_free (add_network_data); - reply = _nm_dbus_proxy_call_finish (proxy, result, - G_VARIANT_TYPE ("(o)"), - &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (!assoc_data) { if (!error) { - gs_free char *net_path = NULL; + const char *net_path; /* the assoc-request was already cancelled, but the AddNetwork request succeeded. * Cleanup the created network. * * This cleanup action does not work when NetworkManager is about to exit * and leaves the mainloop. During program shutdown, we may orphan networks. */ - g_variant_get (reply, "(o)", &net_path); - g_dbus_proxy_call (proxy, - "RemoveNetwork", - g_variant_new ("(o)", net_path), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - NULL, - NULL); + g_variant_get (res, "(&o)", &net_path); + g_dbus_connection_call (G_DBUS_CONNECTION (source), + name_owner->str, + object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE, + "RemoveNetwork", + g_variant_new ("(o)", net_path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + NULL, + NULL, + NULL); } return; } @@ -2150,7 +2131,8 @@ assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat return; } - g_variant_get (reply, "(o)", &priv->net_path); + nm_assert (!priv->net_path); + g_variant_get (res, "(o)", &priv->net_path); /* Send blobs first; otherwise jump to selecting the network */ blobs = nm_supplicant_config_get_blobs (priv->assoc_data->cfg); @@ -2158,7 +2140,10 @@ assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat ? g_hash_table_size (blobs) : 0u; - _LOGT ("assoc[%p]: network added (%s) (%u blobs left)", priv->assoc_data, priv->net_path, priv->assoc_data->blobs_left); + _LOGT ("assoc["NM_HASH_OBFUSCATE_PTR_FMT"]: network added (%s) (%u blobs left)", + NM_HASH_OBFUSCATE_PTR (priv->assoc_data), + priv->net_path, + priv->assoc_data->blobs_left); if (priv->assoc_data->blobs_left == 0) { assoc_call_select_network (self); @@ -2167,29 +2152,28 @@ assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat 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, - nm_utils_gbytes_to_variant_ay (blob_data)), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->assoc_data->cancellable, - (GAsyncReadyCallback) assoc_add_blob_cb, - self); + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "AddBlob", + g_variant_new ("(s@ay)", + blob_name, + nm_utils_gbytes_to_variant_ay (blob_data)), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + priv->assoc_data->cancellable, + assoc_add_blob_cb, + self); } } static void -assoc_set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +assoc_set_ap_scan_cb (GVariant *ret, GError *error, gpointer user_data) { NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - gs_unref_variant GVariant *reply = NULL; - gs_free_error GError *error = NULL; AddNetworkData *add_network_data; - reply = g_dbus_proxy_call_finish (proxy, result, &error); if (nm_utils_error_is_cancelled (error)) return; @@ -2201,23 +2185,36 @@ assoc_set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat return; } - _LOGT ("assoc[%p]: set interface ap_scan to %d", - priv->assoc_data, + _LOGT ("assoc["NM_HASH_OBFUSCATE_PTR_FMT"]: interface ap_scan set to %d", + NM_HASH_OBFUSCATE_PTR (priv->assoc_data), nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg)); - add_network_data = g_slice_new0 (AddNetworkData); + /* the association does not keep @self alive. We want to be able to remove + * the network again, even if @self is already gone. Hence, track the data + * separately. + * + * For that we also have a shutdown_wait_obj so that on exit we still wait + * to handle the response. */ + add_network_data = g_slice_new (AddNetworkData); + *add_network_data = (AddNetworkData) { + .assoc_data = priv->assoc_data, + .name_owner = nm_ref_string_ref (priv->name_owner), + .object_path = nm_ref_string_ref (priv->object_path), + .shutdown_wait_obj = g_object_new (G_TYPE_OBJECT, NULL), + }; + nm_shutdown_wait_obj_register_object (add_network_data->shutdown_wait_obj, "supplicant-add-network"); priv->assoc_data->add_network_data = add_network_data; - add_network_data->assoc_data = priv->assoc_data; - - g_dbus_proxy_call (priv->iface_proxy, - "AddNetwork", - g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->assoc_data->cfg)), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) assoc_add_network_cb, - add_network_data); + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "AddNetwork", + g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->assoc_data->cfg)), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + NULL, + assoc_add_network_cb, + add_network_data); } static gboolean @@ -2264,49 +2261,51 @@ nm_supplicant_interface_assoc (NMSupplicantInterface *self, nm_supplicant_interface_disconnect (self); - assoc_data = g_slice_new0 (AssocData); - priv->assoc_data = assoc_data; + assoc_data = g_slice_new (AssocData); + *assoc_data = (AssocData) { + .self = self, + .cfg = g_object_ref (cfg), + .callback = callback, + .user_data = user_data, + }; - assoc_data->self = self; - assoc_data->cfg = g_object_ref (cfg); - assoc_data->callback = callback; - assoc_data->user_data = user_data; + priv->assoc_data = assoc_data; - _LOGD ("assoc[%p]: starting association...", assoc_data); + _LOGD ("assoc["NM_HASH_OBFUSCATE_PTR_FMT"]: starting association...", + NM_HASH_OBFUSCATE_PTR (assoc_data)); - /* Make sure the supplicant supports EAP-FAST before trying to send - * it an EAP-FAST configuration. - */ if ( _get_capability (priv, NM_SUPPL_CAP_TYPE_FAST) == NM_TERNARY_FALSE && nm_supplicant_config_fast_required (cfg)) { + /* Make sure the supplicant supports EAP-FAST before trying to send + * it an EAP-FAST configuration. + */ assoc_data->fail_on_idle_id = g_idle_add (assoc_fail_on_idle_cb, self); return; } assoc_data->cancellable = g_cancellable_new(); - g_dbus_proxy_call (priv->iface_proxy, - DBUS_INTERFACE_PROPERTIES ".Set", - g_variant_new ("(ssv)", - NM_WPAS_DBUS_IFACE_INTERFACE, - "ApScan", - g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg))), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->assoc_data->cancellable, - (GAsyncReadyCallback) assoc_set_ap_scan_cb, - self); + nm_dbus_connection_call_set (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE, + "ApScan", + g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg)), + DBUS_TIMEOUT_MSEC, + assoc_data->cancellable, + assoc_set_ap_scan_cb, + self); } /*****************************************************************************/ static void -scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +scan_request_cb (GObject *source, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; - gs_unref_variant GVariant *reply = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; - reply = g_dbus_proxy_call_finish (proxy, result, &error); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (nm_utils_error_is_cancelled (error)) return; @@ -2350,14 +2349,16 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, 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); + _dbus_connection_call (self, + NM_WPAS_DBUS_IFACE_INTERFACE, + "Scan", + g_variant_new ("(a{sv})", &builder), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT_MSEC, + priv->main_cancellable, + scan_request_cb, + self); } /*****************************************************************************/ @@ -2370,7 +2371,23 @@ nm_supplicant_interface_get_state (NMSupplicantInterface * self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state; } -const char * +void +_nm_supplicant_interface_set_state_down (NMSupplicantInterface * self, + gboolean force_remove_from_supplicant, + const char *reason) +{ + set_state_down (self, force_remove_from_supplicant, reason); +} + +NMRefString * +nm_supplicant_interface_get_name_owner (NMSupplicantInterface *self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL); + + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->name_owner; +} + +NMRefString * nm_supplicant_interface_get_object_path (NMSupplicantInterface *self) { g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL); @@ -2383,7 +2400,7 @@ 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; + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->ifname; } guint @@ -2400,133 +2417,478 @@ void nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, guint timeout) { - NMSupplicantInterfacePrivate *priv; GVariantBuilder builder; g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); g_return_if_fail (timeout > 0 && timeout <= 600); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", "Timeout", g_variant_new_int32 (timeout)); - g_dbus_proxy_call (priv->p2p_proxy, - "Find", - g_variant_new ("(a{sv})", &builder), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->other_cancellable, - (GAsyncReadyCallback) log_result_cb, - self); + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + "Find", + g_variant_new ("(a{sv})", &builder), + G_VARIANT_TYPE ("()"), + "p2p-find"); } void nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self) { - NMSupplicantInterfacePrivate *priv; - g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - g_dbus_proxy_call (priv->p2p_proxy, - "StopFind", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->other_cancellable, - (GAsyncReadyCallback) scan_request_cb, - self); + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + "StopFind", + NULL, + G_VARIANT_TYPE ("()"), + "p2p-stop-find"); } /*****************************************************************************/ void -nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, - const char * peer, - const char * wps_method, - const char * wps_pin) +nm_supplicant_interface_p2p_connect (NMSupplicantInterface *self, + const char *peer, + const char *wps_method, + const char *wps_pin) { - NMSupplicantInterfacePrivate *priv; GVariantBuilder builder; g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - /* Don't do anything if there is no connection to the supplicant yet. */ - if (!priv->p2p_proxy || !priv->object_path) - return; - - /* Connect parameters */ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", "wps_method", g_variant_new_string (wps_method)); - if (wps_pin) g_variant_builder_add (&builder, "{sv}", "pin", g_variant_new_string (wps_pin)); - g_variant_builder_add (&builder, "{sv}", "peer", g_variant_new_object_path (peer)); - g_variant_builder_add (&builder, "{sv}", "join", g_variant_new_boolean (FALSE)); g_variant_builder_add (&builder, "{sv}", "persistent", g_variant_new_boolean (FALSE)); g_variant_builder_add (&builder, "{sv}", "go_intent", g_variant_new_int32 (7)); - g_dbus_proxy_call (priv->p2p_proxy, - "Connect", - g_variant_new ("(a{sv})", &builder), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->other_cancellable, - (GAsyncReadyCallback) log_result_cb, - "p2p connect"); + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + "Connect", + g_variant_new ("(a{sv})", &builder), + G_VARIANT_TYPE ("()"), + "p2p-connect"); } void nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self) { - NMSupplicantInterfacePrivate *priv; + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + "Cancel", + NULL, + G_VARIANT_TYPE ("()"), + "p2p-cancel"); +} +void +nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self) +{ g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + _dbus_connection_call_simple (self, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + "Disconnect", + NULL, + G_VARIANT_TYPE ("()"), + "p2p-disconnect"); +} + +/*****************************************************************************/ - /* Don't do anything if there is no connection to the supplicant yet. */ - if (!priv->p2p_proxy || !priv->object_path) +static void +_properties_changed (NMSupplicantInterface *self, + const char *interface_name, + GVariant *properties, + gboolean initial) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + gboolean is_main; + gboolean old_val_scanning; + gboolean old_val_p2p_available; + + nm_assert (!properties || g_variant_is_of_type (properties, G_VARIANT_TYPE ("a{sv}"))); + + if (initial) + priv->starting_pending_count--; + + if ( (initial || priv->is_ready_main) + && nm_streq (interface_name, NM_WPAS_DBUS_IFACE_INTERFACE)) + is_main = TRUE; + else if ( (initial || priv->is_ready_p2p_device) + && nm_streq (interface_name, NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE)) { + nm_assert (_get_capability (priv, NM_SUPPL_CAP_TYPE_P2P) == NM_TERNARY_TRUE); + is_main = FALSE; + } else return; - g_dbus_proxy_call (priv->p2p_proxy, - "Cancel", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->other_cancellable, - (GAsyncReadyCallback) log_result_cb, - "cancel p2p connect"); + g_object_freeze_notify (G_OBJECT (self)); + + priv->starting_pending_count++; + + old_val_scanning = _prop_scanning_get (priv); + old_val_p2p_available = _prop_p2p_available_get (priv); + + if (is_main) { + priv->is_ready_main = TRUE; + _properties_changed_main (self, properties); + } else { + priv->is_ready_p2p_device = TRUE; + _properties_changed_p2p_device (self, properties); + } + + priv->starting_pending_count--; + _starting_check_ready (self); + + if (old_val_scanning != _prop_scanning_get (priv)) + _notify (self, PROP_SCANNING); + if (old_val_p2p_available != _prop_p2p_available_get (priv)) + _notify (self, PROP_P2P_AVAILABLE); + + g_object_thaw_notify (G_OBJECT (self)); } -void -nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self) +static void +_properties_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { - NMSupplicantInterfacePrivate *priv; + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + const char *interface_name; + gs_unref_variant GVariant *changed_properties = NULL; - g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) + return; - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + g_variant_get (parameters, + "(&s@a{sv}^a&s)", + &interface_name, + &changed_properties, + NULL); + _properties_changed (self, + interface_name, + changed_properties, + FALSE); +} + +static void +_bss_properties_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + nm_auto_ref_string NMRefString *bss_path = NULL; + gs_unref_variant GVariant *changed_properties = NULL; + NMSupplicantBssInfo *bss_info; - /* Don't do anything if there is no connection to the supplicant. */ - if (!priv->p2p_proxy || !priv->object_path) + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) return; - g_dbus_proxy_call (priv->p2p_proxy, - "Disconnect", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->other_cancellable, - (GAsyncReadyCallback) log_result_cb, - "p2p disconnect"); + bss_path = nm_ref_string_new (object_path); + + bss_info = g_hash_table_lookup (priv->bss_idx, &bss_path); + if (!bss_info) + return; + if (bss_info->_init_cancellable) + return; + + g_variant_get (parameters, + "(&s@a{sv}^a&s)", + NULL, + &changed_properties, + NULL); + _bss_info_properties_changed (self, bss_info, changed_properties, FALSE); +} + +static void +_peer_properties_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + nm_auto_ref_string NMRefString *peer_path = NULL; + gs_unref_variant GVariant *changed_properties = NULL; + NMSupplicantPeerInfo *peer_info; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) + return; + + peer_path = nm_ref_string_new (object_path); + + peer_info = g_hash_table_lookup (priv->peer_idx, &peer_path); + if (!peer_info) + return; + if (peer_info->_init_cancellable) + return; + + g_variant_get (parameters, + "(&s@a{sv}^a&s)", + NULL, + &changed_properties, + NULL); + _peer_info_properties_changed (self, peer_info, changed_properties, FALSE); +} + +static void +_get_all_main_cb (GVariant *result, + GError *error, + gpointer user_data) +{ + gs_unref_variant GVariant *properties = NULL; + + if (nm_utils_error_is_cancelled (error)) + return; + + if (result) + g_variant_get (result, "(@a{sv})", &properties); + _properties_changed (user_data, + NM_WPAS_DBUS_IFACE_INTERFACE, + properties, + TRUE); +} + +static void +_get_all_p2p_device_cb (GVariant *result, + GError *error, + gpointer user_data) +{ + gs_unref_variant GVariant *properties = NULL; + + if (nm_utils_error_is_cancelled (error)) + return; + + if (result) + g_variant_get (result, "(@a{sv})", &properties); + _properties_changed (user_data, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + properties, + TRUE); +} + +static void +_signal_handle (NMSupplicantInterface *self, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char *path; + + if (nm_streq (signal_interface_name, NM_WPAS_DBUS_IFACE_INTERFACE)) { + + if (!priv->is_ready_main) + return; + + if (nm_streq (signal_name, "ScanDone")) { + priv->last_scan_msec = nm_utils_get_monotonic_timestamp_msec (); + _LOGT ("ScanDone signal received"); + if (priv->state > NM_SUPPLICANT_INTERFACE_STATE_STARTING) { + nm_assert (priv->state < NM_SUPPLICANT_INTERFACE_STATE_DOWN); + g_signal_emit (self, signals[SCAN_DONE], 0); + } + return; + } + + if (nm_streq (signal_name, "BSSAdded")) { + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oa{sv})"))) + return; + + g_variant_get (parameters, "(&oa{sv})", &path, NULL); + _bss_info_add (self, path); + return; + } + + if (nm_streq (signal_name, "BSSRemoved")) { + nm_auto_ref_string NMRefString *bss_path = NULL; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) + return; + + g_variant_get (parameters, "(&o)", &path); + bss_path = nm_ref_string_new (path); + _bss_info_remove (self, &bss_path); + return; + } + + if (nm_streq (signal_name, "EAP")) { + NMSupplicantAuthState auth_state = NM_SUPPLICANT_AUTH_STATE_UNKNOWN; + const char *status; + const char *parameter; + + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss)"))) + return; + + g_variant_get (parameters, "(&s&s)", &status, ¶meter); + + if (nm_streq (status, "started")) + auth_state = NM_SUPPLICANT_AUTH_STATE_STARTED; + else if (nm_streq (status, "completion")) { + if (nm_streq (parameter, "success")) + auth_state = NM_SUPPLICANT_AUTH_STATE_SUCCESS; + else if (nm_streq (parameter, "failure")) + auth_state = NM_SUPPLICANT_AUTH_STATE_FAILURE; + } + + /* the state eventually reaches one of started, success or failure + * so ignore any other intermediate (unknown) state change. */ + if ( auth_state != NM_SUPPLICANT_AUTH_STATE_UNKNOWN + && auth_state != priv->auth_state) { + priv->auth_state = auth_state; + _notify (self, PROP_AUTH_STATE); + } + return; + } + + return; + } + + if (nm_streq (signal_interface_name, NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE)) { + + if (!priv->is_ready_p2p_device) + return; + + if (nm_streq (signal_name, "DeviceFound")) { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) { + g_variant_get (parameters, "(&o)", &path); + _peer_info_add (self, path); + } + return; + } + + if (nm_streq (signal_name, "DeviceLost")) { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) { + nm_auto_ref_string NMRefString *peer_path = NULL; + + g_variant_get (parameters, "(&o)", &path); + peer_path = nm_ref_string_new (path); + _peer_info_remove (self, &peer_path); + } + return; + } + + if (nm_streq (signal_name, "GroupStarted")) { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})"))) { + gs_unref_variant GVariant *args = NULL; + gs_unref_object NMSupplicantInterface *iface = NULL; + const char *group_path; + const char *iface_path; + + g_variant_get (parameters, "(@a{sv})", &args); + if (!g_variant_lookup (args, "group_object", "&o", &group_path)) + return; + if (!g_variant_lookup (args, "interface_object", "&o", &iface_path)) + return; + + if (nm_streq (iface_path, priv->object_path->str)) { + _LOGW ("P2P: GroupStarted on existing interface"); + iface = g_object_ref (self); + } else { + iface = nm_supplicant_manager_create_interface_from_path (priv->supplicant_manager, + iface_path); + if (iface == NULL) { + _LOGW ("P2P: Group interface already exists in GroupStarted handler, aborting further processing."); + return; + } + } + + /* Signal existence of the (new) interface. */ + g_signal_emit (self, signals[GROUP_STARTED], 0, iface); + } + return; + } + + if (nm_streq (signal_name, "GroupFinished")) { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})"))) { + gs_unref_variant GVariant *args = NULL; + const char *iface_path; + + g_variant_get (parameters, "(@a{sv})", &args); + + /* TODO: Group finished is called on the management interface! + * This means the signal consumer will currently need to assume which + * interface is finishing or it needs to match the object paths. + */ + if (!g_variant_lookup (args, "interface_object", "&o", &iface_path)) + return; + + _LOGD ("P2P: GroupFinished signal on interface %s for interface %s", priv->object_path->str, iface_path); + + /* Signal group finish interface (on management interface). */ + g_signal_emit (self, signals[GROUP_FINISHED], 0, iface_path); + } + return; + } + + return; + } +} + +static void +_signal_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMSupplicantInterface *self = user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->starting_pending_count++; + + _signal_handle (self, signal_interface_name, signal_name, parameters); + + priv->starting_pending_count--; + _starting_check_ready (self); +} + +/*****************************************************************************/ + +gboolean +nm_supplicant_interface_get_p2p_available (NMSupplicantInterface *self) +{ + return _prop_p2p_available_get (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)); +} + +gboolean +nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self) +{ + return _prop_p2p_group_joined_get (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)); +} + +const char* +nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self) +{ + return nm_ref_string_get_str (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_group_path); +} + +gboolean +nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self) +{ + return _prop_p2p_group_is_owner_get (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)); } /*****************************************************************************/ @@ -2537,26 +2899,27 @@ get_property (GObject *object, GValue *value, GParamSpec *pspec) { - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object); + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (object); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); switch (prop_id) { case PROP_SCANNING: - g_value_set_boolean (value, priv->scanning); + g_value_set_boolean (value, nm_supplicant_interface_get_scanning (self)); break; case PROP_CURRENT_BSS: - g_value_set_string (value, priv->current_bss); + g_value_set_string (value, nm_ref_string_get_str (nm_supplicant_interface_get_current_bss (self))); break; case PROP_P2P_GROUP_JOINED: - g_value_set_boolean (value, priv->p2p_capable && priv->group_proxy_acquired); + g_value_set_boolean (value, nm_supplicant_interface_get_p2p_group_joined (self)); break; case PROP_P2P_GROUP_PATH: - g_value_set_string (value, nm_supplicant_interface_get_p2p_group_path (NM_SUPPLICANT_INTERFACE (object))); + g_value_set_string (value, nm_supplicant_interface_get_p2p_group_path (self)); break; case PROP_P2P_GROUP_OWNER: - g_value_set_boolean (value, priv->p2p_group_owner); + g_value_set_boolean (value, nm_supplicant_interface_get_p2p_group_owner (self)); break; case PROP_P2P_AVAILABLE: - g_value_set_boolean (value, priv->p2p_capable && priv->p2p_proxy_acquired); + g_value_set_boolean (value, nm_supplicant_interface_get_p2p_available (self)); break; case PROP_AUTH_STATE: g_value_set_uint (value, priv->auth_state); @@ -2576,21 +2939,31 @@ set_property (GObject *object, NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object); switch (prop_id) { - case PROP_IFACE: + case PROP_SUPPLICANT_MANAGER: /* construct-only */ - priv->dev = g_value_dup_string (value); + priv->supplicant_manager = g_object_ref (g_value_get_pointer (value)); + nm_assert (NM_IS_SUPPLICANT_MANAGER (priv->supplicant_manager)); + + priv->dbus_connection = g_object_ref (nm_supplicant_manager_get_dbus_connection (priv->supplicant_manager)); + nm_assert (G_IS_DBUS_CONNECTION (priv->dbus_connection)); + + priv->name_owner = nm_ref_string_ref (nm_supplicant_manager_get_dbus_name_owner (priv->supplicant_manager)); + nm_assert (NM_IS_REF_STRING (priv->name_owner)); + + priv->global_capabilities = nm_supplicant_manager_get_global_capabilities (priv->supplicant_manager); break; - case PROP_OBJECT_PATH: + case PROP_DBUS_OBJECT_PATH: /* construct-only */ - priv->object_path = g_value_dup_string (value); + priv->object_path = nm_ref_string_ref (g_value_get_pointer (value)); + nm_assert (NM_IS_REF_STRING (priv->object_path)); break; - case PROP_DRIVER: + case PROP_IFINDEX: /* construct-only */ - priv->driver = g_value_get_uint (value); + priv->ifindex = g_value_get_int (value); break; - case PROP_GLOBAL_CAPABILITIES: + case PROP_DRIVER: /* construct-only */ - priv->global_capabilities = g_value_get_uint64 (value); + priv->requested_driver = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -2598,6 +2971,8 @@ set_property (GObject *object, } } +/*****************************************************************************/ + static void nm_supplicant_interface_init (NMSupplicantInterface * self) { @@ -2607,30 +2982,167 @@ nm_supplicant_interface_init (NMSupplicantInterface * self) self->_priv = priv; - c_list_init (&self->supp_lst); - nm_assert (priv->global_capabilities == NM_SUPPL_CAP_MASK_NONE); nm_assert (priv->iface_capabilities == NM_SUPPL_CAP_MASK_NONE); - priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; - priv->bss_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, bss_data_destroy); - priv->peer_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, peer_data_destroy); + priv->state = NM_SUPPLICANT_INTERFACE_STATE_STARTING; + priv->supp_state = NM_SUPPLICANT_INTERFACE_STATE_INVALID; + + c_list_init (&self->supp_lst); + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMSupplicantBssInfo, bss_path) == 0); + priv->bss_idx = g_hash_table_new (nm_pdirect_hash, nm_pdirect_equal); + + c_list_init (&priv->bss_lst_head); + c_list_init (&priv->bss_initializing_lst_head); + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMSupplicantPeerInfo, peer_path) == 0); + priv->peer_idx = g_hash_table_new (nm_pdirect_hash, nm_pdirect_equal); + + c_list_init (&priv->peer_lst_head); + c_list_init (&priv->peer_initializing_lst_head); + + priv->main_cancellable = g_cancellable_new (); +} + +static void +constructed (GObject *object) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (object); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->constructed (object); + + _LOGD ("new supplicant interface %s on %s", + priv->object_path->str, + priv->name_owner->str); + + priv->properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NULL, + _properties_changed_cb, + self, + NULL); + + priv->bss_properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection, + priv->name_owner->str, + NULL, + NM_WPAS_DBUS_IFACE_BSS, + _bss_properties_changed_cb, + self, + NULL); + + priv->signal_id = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner->str, + NULL, + NULL, + priv->object_path->str, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _signal_cb, + self, + NULL); + + /* Scan result aging parameters */ + nm_dbus_connection_call_set (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE, + "BSSExpireAge", + g_variant_new_uint32 (250), + DBUS_TIMEOUT_MSEC, + NULL, + NULL, + NULL); + nm_dbus_connection_call_set (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE, + "BSSExpireCount", + g_variant_new_uint32 (2), + DBUS_TIMEOUT_MSEC, + NULL, + NULL, + NULL); + + if (_get_capability (priv, NM_SUPPL_CAP_TYPE_PMF) == NM_TERNARY_TRUE) { + /* Initialize global PMF setting to 'optional' */ + nm_dbus_connection_call_set (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE, + "Pmf", + g_variant_new_string ("1"), + DBUS_TIMEOUT_MSEC, + NULL, + NULL, + NULL); + } + + if (_get_capability (priv, NM_SUPPL_CAP_TYPE_AP) == NM_TERNARY_DEFAULT) { + /* 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->starting_pending_count++; + _dbus_connection_call (self, + DBUS_INTERFACE_INTROSPECTABLE, + "Introspect", + NULL, + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + 5000, + priv->main_cancellable, + iface_introspect_cb, + self); + } + + priv->starting_pending_count++; + nm_dbus_connection_call_get_all (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE, + 5000, + priv->main_cancellable, + _get_all_main_cb, + self); + + if (_get_capability (priv, NM_SUPPL_CAP_TYPE_P2P) == NM_TERNARY_TRUE) { + priv->peer_properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection, + priv->name_owner->str, + NULL, + NM_WPAS_DBUS_IFACE_PEER, + _peer_properties_changed_cb, + self, + NULL); + + priv->starting_pending_count++; + nm_dbus_connection_call_get_all (priv->dbus_connection, + priv->name_owner->str, + priv->object_path->str, + NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + 5000, + priv->main_cancellable, + _get_all_p2p_device_cb, + self); + } } NMSupplicantInterface * -nm_supplicant_interface_new (const char *ifname, - const char *object_path, - NMSupplicantDriver driver, - NMSupplCapMask global_capabilities) +nm_supplicant_interface_new (NMSupplicantManager *supplicant_manager, + NMRefString *object_path, + int ifindex, + NMSupplicantDriver driver) { - /* One of ifname or path need to be set */ - g_return_val_if_fail ((ifname != NULL) != (object_path != NULL), NULL); + nm_assert (NM_IS_SUPPLICANT_MANAGER (supplicant_manager)); return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, - NM_SUPPLICANT_INTERFACE_IFACE, ifname, - NM_SUPPLICANT_INTERFACE_OBJECT_PATH, object_path, + NM_SUPPLICANT_INTERFACE_SUPPLICANT_MANAGER, supplicant_manager, + NM_SUPPLICANT_INTERFACE_DBUS_OBJECT_PATH, object_path, + NM_SUPPLICANT_INTERFACE_IFINDEX, ifindex, NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver, - NM_SUPPLICANT_INTERFACE_GLOBAL_CAPABILITIES, (guint64) global_capabilities, NULL); } @@ -2640,9 +3152,11 @@ dispose (GObject *object) NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (object); NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + if (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DOWN) + set_state_down (self, TRUE, "NMSupplicantInterface is disposing"); + nm_assert (c_list_is_empty (&self->supp_lst)); - nm_supplicant_interface_cancel_wps (self); if (priv->wps_data) { /* we shut down, but an asynchronous Cancel request is pending. * We don't want to cancel it, so mark wps-data that @self is gone. @@ -2652,38 +3166,21 @@ dispose (GObject *object) priv->wps_data = NULL; } - if (priv->assoc_data) { - gs_free_error GError *error = NULL; - - nm_utils_error_set_cancelled (&error, TRUE, "NMSupplicantInterface"); - assoc_return (self, error, "cancelled due to dispose of supplicant interface"); - } - - if (priv->iface_proxy) - g_signal_handlers_disconnect_by_data (priv->iface_proxy, object); - g_clear_object (&priv->iface_proxy); - if (priv->p2p_proxy) - g_signal_handlers_disconnect_by_data (priv->p2p_proxy, object); - g_clear_object (&priv->p2p_proxy); - if (priv->group_proxy) - g_signal_handlers_disconnect_by_data (priv->group_proxy, object); - g_clear_object (&priv->group_proxy); + nm_assert (!priv->assoc_data); - nm_clear_g_cancellable (&priv->init_cancellable); - nm_clear_g_cancellable (&priv->other_cancellable); + g_clear_pointer (&priv->bss_idx, g_hash_table_destroy); + g_clear_pointer (&priv->peer_idx, g_hash_table_destroy); - if (priv->wpas_proxy) - g_signal_handlers_disconnect_by_data (priv->wpas_proxy, object); - g_clear_object (&priv->wpas_proxy); - g_clear_pointer (&priv->bss_proxies, g_hash_table_destroy); - g_clear_pointer (&priv->peer_proxies, 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); + nm_clear_pointer (&priv->current_bss, nm_ref_string_unref); G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object); + + nm_clear_pointer (&priv->object_path, nm_ref_string_unref); + nm_clear_pointer (&priv->name_owner, nm_ref_string_unref); + g_clear_object (&priv->supplicant_manager); + g_clear_object (&priv->dbus_connection); + nm_clear_g_free (&priv->ifname); + nm_assert (!priv->net_path); } static void @@ -2693,10 +3190,34 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) g_type_class_add_private (object_class, sizeof (NMSupplicantInterfacePrivate)); + object_class->constructed = constructed; object_class->dispose = dispose; object_class->set_property = set_property; object_class->get_property = get_property; + obj_properties[PROP_SUPPLICANT_MANAGER] = + g_param_spec_pointer (NM_SUPPLICANT_INTERFACE_SUPPLICANT_MANAGER, "", "", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_DBUS_OBJECT_PATH] = + g_param_spec_pointer (NM_SUPPLICANT_INTERFACE_DBUS_OBJECT_PATH, "", "", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_IFINDEX] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_IFINDEX, "", "", + 0, G_MAXINT, 0, + 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_SCANNING] = g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_SCANNING, "", "", FALSE, @@ -2707,18 +3228,6 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 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_OBJECT_PATH] = - g_param_spec_string (NM_SUPPLICANT_INTERFACE_OBJECT_PATH, "", "", - NULL, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); obj_properties[PROP_P2P_GROUP_JOINED] = g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, "", "", FALSE, @@ -2734,25 +3243,11 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) FALSE, G_PARAM_READABLE | 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_P2P_AVAILABLE] = g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, "", "", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_GLOBAL_CAPABILITIES] = - g_param_spec_uint64 (NM_SUPPLICANT_INTERFACE_GLOBAL_CAPABILITIES, "", "", - 0, - NM_SUPPL_CAP_MASK_ALL, - 0, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); obj_properties[PROP_AUTH_STATE] = g_param_spec_uint (NM_SUPPLICANT_INTERFACE_AUTH_STATE, "", "", NM_SUPPLICANT_AUTH_STATE_UNKNOWN, @@ -2771,45 +3266,21 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 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, + signals[BSS_CHANGED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_CHANGED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); - signals[BSS_UPDATED] = - g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED, + signals[PEER_CHANGED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_CHANGED, 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[PEER_UPDATED] = - g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_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[PEER_REMOVED] = - g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_STRING); + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); signals[SCAN_DONE] = g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE, @@ -2817,7 +3288,7 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + G_TYPE_NONE, 0); signals[WPS_CREDENTIALS] = g_signal_new (NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS, diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index 25dca98481..8964a4754f 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -16,10 +16,9 @@ * A mix of wpa_supplicant interface states and internal states. */ typedef enum { - NM_SUPPLICANT_INTERFACE_STATE_INVALID = -1, - NM_SUPPLICANT_INTERFACE_STATE_INIT = 0, - NM_SUPPLICANT_INTERFACE_STATE_STARTING, - NM_SUPPLICANT_INTERFACE_STATE_READY, + NM_SUPPLICANT_INTERFACE_STATE_INVALID = 0, + + NM_SUPPLICANT_INTERFACE_STATE_STARTING = 1, NM_SUPPLICANT_INTERFACE_STATE_DISABLED, NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, @@ -35,6 +34,13 @@ typedef enum { NM_SUPPLICANT_INTERFACE_STATE_DOWN, } NMSupplicantInterfaceState; +static inline gboolean +NM_SUPPLICANT_INTERFACE_STATE_IS_OPERATIONAL (NMSupplicantInterfaceState state) +{ + return state > NM_SUPPLICANT_INTERFACE_STATE_STARTING + && state < NM_SUPPLICANT_INTERFACE_STATE_DOWN; +} + typedef enum { NM_SUPPLICANT_AUTH_STATE_UNKNOWN, NM_SUPPLICANT_AUTH_STATE_STARTED, @@ -50,8 +56,9 @@ typedef enum { #define NM_IS_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_INTERFACE)) #define NM_SUPPLICANT_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass)) -#define NM_SUPPLICANT_INTERFACE_IFACE "iface" -#define NM_SUPPLICANT_INTERFACE_OBJECT_PATH "object-path" +#define NM_SUPPLICANT_INTERFACE_SUPPLICANT_MANAGER "supplicant-manager" +#define NM_SUPPLICANT_INTERFACE_DBUS_OBJECT_PATH "dbus-object-path" +#define NM_SUPPLICANT_INTERFACE_IFINDEX "ifindex" #define NM_SUPPLICANT_INTERFACE_SCANNING "scanning" #define NM_SUPPLICANT_INTERFACE_CURRENT_BSS "current-bss" #define NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED "p2p-group-joined" @@ -59,15 +66,11 @@ typedef enum { #define NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER "p2p-group-owner" #define NM_SUPPLICANT_INTERFACE_DRIVER "driver" #define NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE "p2p-available" -#define NM_SUPPLICANT_INTERFACE_GLOBAL_CAPABILITIES "global-capabilities" #define NM_SUPPLICANT_INTERFACE_AUTH_STATE "auth-state" #define NM_SUPPLICANT_INTERFACE_STATE "state" -#define NM_SUPPLICANT_INTERFACE_REMOVED "removed" -#define NM_SUPPLICANT_INTERFACE_BSS_UPDATED "bss-updated" -#define NM_SUPPLICANT_INTERFACE_BSS_REMOVED "bss-removed" -#define NM_SUPPLICANT_INTERFACE_PEER_UPDATED "peer-updated" -#define NM_SUPPLICANT_INTERFACE_PEER_REMOVED "peer-removed" +#define NM_SUPPLICANT_INTERFACE_BSS_CHANGED "bss-changed" +#define NM_SUPPLICANT_INTERFACE_PEER_CHANGED "peer-changed" #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS "wps-credentials" #define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started" @@ -85,13 +88,17 @@ struct _NMSupplicantInterface { GType nm_supplicant_interface_get_type (void); -NMSupplicantInterface *nm_supplicant_interface_new (const char *ifname, - const char *object_path, - NMSupplicantDriver driver, - NMSupplCapMask global_capabilities); +NMSupplicantInterface *nm_supplicant_interface_new (NMSupplicantManager *supplicant_manager, + NMRefString *object_path, + int ifindex, + NMSupplicantDriver driver); + +NMRefString *nm_supplicant_interface_get_name_owner (NMSupplicantInterface *self); +NMRefString *nm_supplicant_interface_get_object_path (NMSupplicantInterface * iface); -void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, - gboolean available); +void _nm_supplicant_interface_set_state_down (NMSupplicantInterface * self, + gboolean force_remove_from_supplicant, + const char *reason); typedef void (*NMSupplicantInterfaceAssocCb) (NMSupplicantInterface *iface, GError *error, @@ -115,8 +122,6 @@ nm_supplicant_interface_disconnect_async (NMSupplicantInterface * self, NMSupplicantInterfaceDisconnectCb callback, gpointer user_data); -const char *nm_supplicant_interface_get_object_path (NMSupplicantInterface * iface); - void nm_supplicant_interface_request_scan (NMSupplicantInterface *self, GBytes *const*ssids, guint ssids_len); @@ -127,7 +132,7 @@ const char *nm_supplicant_interface_state_to_string (NMSupplicantInterfaceState gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self); -const char *nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self); +NMRefString *nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self); gint64 nm_supplicant_interface_get_last_scan (NMSupplicantInterface *self); @@ -135,6 +140,8 @@ const char *nm_supplicant_interface_get_ifname (NMSupplicantInterface *self); guint nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self); +gboolean nm_supplicant_interface_get_p2p_available (NMSupplicantInterface *self); + gboolean nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self); const char* nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self); diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c index 08bed98cf3..2f089fb36f 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -8,20 +8,64 @@ #include "nm-supplicant-manager.h" +#include "nm-core-internal.h" +#include "nm-dbus-manager.h" +#include "nm-glib-aux/nm-dbus-aux.h" +#include "nm-glib-aux/nm-ref-string.h" #include "nm-supplicant-interface.h" #include "nm-supplicant-types.h" -#include "nm-core-internal.h" +#include "platform/nm-platform.h" /*****************************************************************************/ -typedef struct { - GDBusProxy *proxy; +#define CREATE_IFACE_TRY_COUNT_MAX 7u + +struct _NMSupplMgrCreateIfaceHandle { + NMSupplicantManager *self; + CList create_iface_lst; GCancellable *cancellable; + NMSupplicantManagerCreateInterfaceCb callback; + gpointer callback_user_data; + NMShutdownWaitObjHandle *shutdown_handle; + NMRefString *name_owner; + GError *fail_on_idle_error; + NMSupplicantDriver driver; + int ifindex; + guint fail_on_idle_id; + guint create_iface_try_count:5; +}; + +enum { + AVAILABLE_CHANGED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + GDBusConnection *dbus_connection; + + NMRefString *name_owner; + + GCancellable *get_name_owner_cancellable; + GCancellable *get_capabilities_cancellable; + GCancellable *poke_name_owner_cancellable; + + GHashTable *supp_ifaces; CList supp_lst_head; + + CList create_iface_lst_head; + NMSupplCapMask capabilities; - guint die_count_reset_id; - guint die_count; - bool running:1; + + guint name_owner_changed_id; + guint interface_removed_id; + guint poke_name_owner_timeout_id; + guint available_reset_id; + + /* see nm_supplicant_manager_get_available(). */ + NMTernary available:2; + } NMSupplicantManagerPrivate; struct _NMSupplicantManager { @@ -37,6 +81,8 @@ G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT) #define NM_SUPPLICANT_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSupplicantManager, NM_IS_SUPPLICANT_MANAGER) +NM_DEFINE_SINGLETON_GETTER (NMSupplicantManager, nm_supplicant_manager_get, NM_TYPE_SUPPLICANT_MANAGER); + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_SUPPLICANT @@ -48,6 +94,27 @@ NM_CACHED_QUARK_FCN ("nm-supplicant-error-quark", nm_supplicant_error_quark) /*****************************************************************************/ +static void _create_iface_proceed_all (NMSupplicantManager *self, + GError *error); +static void _supp_iface_add (NMSupplicantManager *self, + NMRefString *iface_path, + NMSupplicantInterface *supp_iface); +static void _supp_iface_remove_one (NMSupplicantManager *self, + NMSupplicantInterface *supp_iface, + gboolean force_remove_from_supplicant, + const char *reason); +static void _create_iface_dbus_call_get_interface (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle, + const char *ifname); +static void _create_iface_dbus_call_create_interface (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle, + const char *ifname); +static gboolean _create_iface_fail_on_idle_cb (gpointer user_data); + +static gboolean _available_reset_cb (gpointer user_data); + +/*****************************************************************************/ + NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_driver_to_string, NMSupplicantDriver, NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), NM_UTILS_LOOKUP_ITEM (NM_SUPPLICANT_DRIVER_UNKNOWN, "???"), @@ -58,6 +125,39 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_driver_to_string, NMSupplicantDriver, /*****************************************************************************/ +NMTernary +nm_supplicant_manager_is_available (NMSupplicantManager *self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NM_TERNARY_FALSE); + + return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->available; +} + +NMRefString * +nm_supplicant_manager_get_dbus_name_owner (NMSupplicantManager *self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); + + return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->name_owner; +} + +GDBusConnection *nm_supplicant_manager_get_dbus_connection (NMSupplicantManager *self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); + + return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_connection; +} + +NMSupplCapMask +nm_supplicant_manager_get_global_capabilities (NMSupplicantManager *self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NM_SUPPL_CAP_MASK_NONE); + + return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->capabilities; +} + +/*****************************************************************************/ + static void _caps_set (NMSupplicantManagerPrivate *priv, NMSupplCapType type, @@ -82,66 +182,52 @@ _caps_to_str (NMSupplicantManagerPrivate *priv, /*****************************************************************************/ -static gboolean -die_count_exceeded (guint32 count) +static void +_dbus_call_remove_interface (GDBusConnection *dbus_connection, + const char *name_owner, + const char *iface_path) { - return count > 2; + nm_assert (G_IS_DBUS_CONNECTION (dbus_connection)); + nm_assert (name_owner); + nm_assert (iface_path); + + g_dbus_connection_call (dbus_connection, + name_owner, + NM_WPAS_DBUS_PATH, + NM_WPAS_DBUS_INTERFACE, + "RemoveInterface", + g_variant_new ("(o)", iface_path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 10000, + NULL, + NULL, + NULL); } -static gboolean -is_available (NMSupplicantManager *self) +void +_nm_supplicant_manager_dbus_call_remove_interface (NMSupplicantManager *self, + const char *name_owner, + const char *iface_path) { - NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - - return priv->running - && !die_count_exceeded (priv->die_count); + _dbus_call_remove_interface (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_connection, + name_owner, + iface_path); } /*****************************************************************************/ static void -_sup_iface_last_ref (gpointer data, - GObject *object, - gboolean is_last_ref) -{ - NMSupplicantManager *self = data; - NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - NMSupplicantInterface *sup_iface = NM_SUPPLICANT_INTERFACE (object); - const char *op; - - nm_assert (is_last_ref); - nm_assert (c_list_contains (&priv->supp_lst_head, &sup_iface->supp_lst)); - - c_list_unlink (&sup_iface->supp_lst); - - if ( priv->running - && priv->proxy - && (op = nm_supplicant_interface_get_object_path (sup_iface))) { - g_dbus_proxy_call (priv->proxy, - "RemoveInterface", - g_variant_new ("(o)", op), - G_DBUS_CALL_FLAGS_NONE, - 3000, - NULL, - NULL, - NULL); - } - - g_object_remove_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self); -} - -static void on_supplicant_wfd_ies_set (GObject *source_object, - GAsyncResult *res, + GAsyncResult *result, gpointer user_data) { - gs_unref_variant GVariant *result = NULL; + gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; - result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error); - - if (!result) - _LOGW ("failed to set WFD IEs on wpa_supplicant: %s", error->message); + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), result, &error); + if (!res) + _LOGD ("failed to set WFD IEs on wpa_supplicant: %s", error->message); } /** @@ -164,138 +250,672 @@ nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self, priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - _LOGD ("setting WFD IEs for P2P operation"); + if (!priv->name_owner) + return; + + _LOGD ("setting WFD IEs for P2P operation on %s", priv->name_owner->str); g_variant_builder_init (¶ms, G_VARIANT_TYPE ("(ssv)")); - g_variant_builder_add (¶ms, "s", g_dbus_proxy_get_interface_name (priv->proxy)); + g_variant_builder_add (¶ms, "s", NM_WPAS_DBUS_INTERFACE); g_variant_builder_add (¶ms, "s", "WFDIEs"); g_variant_builder_add_value (¶ms, g_variant_new_variant (nm_utils_gbytes_to_variant_ay (wfd_ies))); - g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy), - g_dbus_proxy_get_name (priv->proxy), - g_dbus_proxy_get_object_path (priv->proxy), - "org.freedesktop.DBus.Properties", + g_dbus_connection_call (priv->dbus_connection, + priv->name_owner->str, + NM_WPAS_DBUS_PATH, + DBUS_INTERFACE_PROPERTIES, "Set", g_variant_builder_end (¶ms), - G_VARIANT_TYPE_UNIT, + G_VARIANT_TYPE ("()"), G_DBUS_CALL_FLAGS_NO_AUTO_START, - 1000, + 3000, NULL, on_supplicant_wfd_ies_set, NULL); } -/** - * nm_supplicant_manager_create_interface: - * @self: the #NMSupplicantManager - * @ifname: the interface for which to obtain the supplicant interface - * @is_wireless: whether the interface is supposed to be wireless. - * - * Note: the manager owns a reference to the instance and the only way to - * get the manager to release it, is by dropping all other references - * to the supplicant-interface (or destroying the manager). - * - * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL. - * Must be unrefed at the end. - * */ -NMSupplicantInterface * +/*****************************************************************************/ + +static gboolean +_poke_name_owner_timeout_cb (gpointer user_data) +{ + NMSupplicantManager *self = user_data; + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + gs_free_error GError *error = NULL; + gboolean available_changed = FALSE; + + nm_assert (!priv->name_owner); + + priv->poke_name_owner_timeout_id = 0; + nm_clear_g_cancellable (&priv->poke_name_owner_cancellable); + + _LOGT ("poke service \"%s\" failed for good with timeout%s", + NM_WPAS_DBUS_SERVICE, + (priv->available == NM_TERNARY_DEFAULT) + ? " (set as not available)" + : ""); + + if (priv->available == NM_TERNARY_DEFAULT) { + /* the available flag usually only changes together with the name-owner. + * However, if we tries to poke the service but failed to start it (with + * timeout), was also set it as (hard) not available. */ + priv->available = NM_TERNARY_FALSE; + nm_clear_g_source (&priv->available_reset_id); + priv->available_reset_id = g_timeout_add_seconds (60, + _available_reset_cb, + self); + available_changed = TRUE; + } + + nm_utils_error_set (&error, + NM_UTILS_ERROR_UNKNOWN, + "Failed to D-Bus activate wpa_supplicant service"); + + _create_iface_proceed_all (self, error); + + if (available_changed) { + /* We delay the emitting of the notification after aborting all + * create-iface handles. */ + g_signal_emit (self, signals[AVAILABLE_CHANGED], 0); + } + + return G_SOURCE_REMOVE; +} + +static void +_poke_name_owner_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + + res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if (nm_utils_error_is_cancelled (error)) + return; + + if (!res) + _LOGT ("poke service \"%s\" failed: %s", NM_WPAS_DBUS_SERVICE, error->message); + else + _LOGT ("poke service \"%s\" succeeded", NM_WPAS_DBUS_SERVICE); + + /* in both cases, we react the same: we wait for the name owner to appear + * or hit the timeout. */ +} + +static void +_poke_name_owner (NMSupplicantManager *self) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + if (priv->poke_name_owner_cancellable) + return; + + _LOGT ("poke service \"%s\"...", NM_WPAS_DBUS_SERVICE); + + priv->poke_name_owner_cancellable = g_cancellable_new (); + priv->poke_name_owner_timeout_id = g_timeout_add (3000, + _poke_name_owner_timeout_cb, + self); + nm_dbus_connection_call_start_service_by_name (priv->dbus_connection, + NM_WPAS_DBUS_SERVICE, + 5000, + priv->poke_name_owner_cancellable, + _poke_name_owner_cb, + self); +} + +/*****************************************************************************/ + +static void +_create_iface_complete (NMSupplMgrCreateIfaceHandle *handle, + NMSupplicantInterface *supp_iface, + GError *error) +{ + nm_assert (!supp_iface || NM_IS_SUPPLICANT_INTERFACE (supp_iface)); + nm_assert ((!!supp_iface) != (!!error)); + + c_list_unlink (&handle->create_iface_lst); + + nm_clear_g_source (&handle->fail_on_idle_id); + + if (handle->callback) { + NMSupplicantManagerCreateInterfaceCb callback; + + nm_assert (NM_IS_SUPPLICANT_MANAGER (handle->self)); + + callback = handle->callback; + handle->callback = NULL; + callback (handle->self, + handle, + supp_iface, + error, + handle->callback_user_data); + } + + g_clear_error (&handle->fail_on_idle_error); + + g_clear_object (&handle->self); + + if (handle->shutdown_handle) { + /* we have a pending CreateInterface request. We keep the handle + * instance alive. This is to remove the device again, once the + * request completes. */ + return; + } + + nm_clear_g_cancellable (&handle->cancellable); + nm_ref_string_unref (handle->name_owner); + + nm_g_slice_free_fcn (handle); +} + +static void +_create_iface_add (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle, + const char *iface_path_str, + gboolean created_by_us) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + nm_auto_ref_string NMRefString *iface_path = NULL; + gs_unref_object NMSupplicantInterface *supp_iface = NULL; + + iface_path = nm_ref_string_new (iface_path_str); + + supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path); + if (supp_iface) { + /* Now this is odd... Reuse the same interface. */ + g_object_ref (supp_iface); + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: interface %s on %s created (already existing)", + NM_HASH_OBFUSCATE_PTR (handle), + iface_path_str, + priv->name_owner->str); + _create_iface_complete (handle, supp_iface, NULL); + return; + } + + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: interface %s on %s created%s", + NM_HASH_OBFUSCATE_PTR (handle), + iface_path_str, + priv->name_owner->str, + created_by_us ? " (created by us)" : ""); + + supp_iface = nm_supplicant_interface_new (self, + iface_path, + handle->ifindex, + handle->driver); + + _supp_iface_add (self, iface_path, supp_iface); + + _create_iface_complete (handle, supp_iface, NULL); +} + +static void +_create_iface_dbus_call_get_interface_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *dbus_connection = G_DBUS_CONNECTION (source); + NMSupplMgrCreateIfaceHandle *handle; + NMSupplicantManager *self; + NMSupplicantManagerPrivate *priv; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + const char *iface_path_str; + + res = g_dbus_connection_call_finish (dbus_connection, result, &error); + + if (nm_utils_error_is_cancelled (error)) + return; + + handle = user_data; + nm_assert (handle->callback); + + self = handle->self; + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + nm_assert (handle->name_owner == priv->name_owner); + + if (!res) { + char ifname[NMP_IFNAMSIZ]; + + if ( handle->create_iface_try_count < CREATE_IFACE_TRY_COUNT_MAX + && _nm_dbus_error_has_name (error, NM_WPAS_ERROR_UNKNOWN_IFACE) + && nm_platform_if_indextoname (NM_PLATFORM_GET, handle->ifindex, ifname)) { + /* Before, supplicant told us the interface existed. Was there a race? + * Try again. */ + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call failed to get interface. Try to create it again (ifname \"%s\")", + NM_HASH_OBFUSCATE_PTR (handle), + ifname); + _create_iface_dbus_call_create_interface (self, handle, ifname); + return; + } + + g_clear_object (&handle->cancellable); + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call to get interface failed: %s", + NM_HASH_OBFUSCATE_PTR (handle), + error->message); + _create_iface_complete (handle, NULL, error); + return; + } + + g_clear_object (&handle->cancellable); + + g_variant_get (res, "(&o)", &iface_path_str); + + _create_iface_add (self, handle, iface_path_str, FALSE); +} + +static void +_create_iface_dbus_call_create_interface_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *dbus_connection = G_DBUS_CONNECTION (source); + NMSupplMgrCreateIfaceHandle *handle = user_data; + NMSupplicantManager *self; + NMSupplicantManagerPrivate *priv; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + const char *iface_path_str; + char ifname[NMP_IFNAMSIZ]; + + res = g_dbus_connection_call_finish (dbus_connection, result, &error); + + nm_shutdown_wait_obj_unregister (g_steal_pointer (&handle->shutdown_handle)); + + if (!res) { + if ( handle->callback + && ({ nm_assert (handle->self); TRUE; }) + && _nm_dbus_error_has_name (error, NM_WPAS_ERROR_EXISTS_ERROR) + && nm_platform_if_indextoname (NM_PLATFORM_GET, handle->ifindex, ifname)) { + self = handle->self; + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call failed to create interface. Try to get existing interface (ifname \"%s\")", + NM_HASH_OBFUSCATE_PTR (handle), + ifname); + _create_iface_dbus_call_get_interface (self, handle, ifname); + return; + } + g_clear_object (&handle->cancellable); + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: D-Bus call failed: %s", + NM_HASH_OBFUSCATE_PTR (handle), + error->message); + _create_iface_complete (handle, NULL, error); + return; + } + + g_clear_object (&handle->cancellable); + + self = handle->self; + priv = self + ? NM_SUPPLICANT_MANAGER_GET_PRIVATE (self) + : NULL; + + g_variant_get (res, "(&o)", &iface_path_str); + + if ( !handle->callback + || priv->name_owner != handle->name_owner) { + if (!handle->callback) { + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: request already cancelled but still remove interface %s in %s", + NM_HASH_OBFUSCATE_PTR (handle), + iface_path_str, + handle->name_owner->str); + } else { + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: name owner changed, still remove interface %s in %s", + NM_HASH_OBFUSCATE_PTR (handle), + iface_path_str, + handle->name_owner->str); + nm_utils_error_set (&error, + NM_UTILS_ERROR_UNKNOWN, + "The name owner changed since creating the interface"); + } + _dbus_call_remove_interface (dbus_connection, + handle->name_owner->str, + iface_path_str); + _create_iface_complete (handle, NULL, error); + return; + } + + _create_iface_add (self, handle, iface_path_str, TRUE); +} + +static void +_create_iface_dbus_call_get_interface (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle, + const char *ifname) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + nm_assert (handle->cancellable); + nm_assert (!handle->shutdown_handle); + + g_dbus_connection_call (priv->dbus_connection, + priv->name_owner->str, + NM_WPAS_DBUS_PATH, + NM_WPAS_DBUS_INTERFACE, + "GetInterface", + g_variant_new ("(s)", ifname), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + 5000, + handle->cancellable, + _create_iface_dbus_call_get_interface_cb, + handle); +} + +static void +_create_iface_dbus_call_create_interface (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle, + const char *ifname) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + GVariantBuilder builder; + + nm_assert (priv->name_owner == handle->name_owner); + nm_assert (handle->cancellable); + nm_assert (!handle->shutdown_handle); + nm_assert (handle->create_iface_try_count <= CREATE_IFACE_TRY_COUNT_MAX); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, + "{sv}", + "Driver", + g_variant_new_string (nm_supplicant_driver_to_string (handle->driver))); + g_variant_builder_add (&builder, + "{sv}", + "Ifname", + g_variant_new_string (ifname)); + + handle->shutdown_handle = nm_shutdown_wait_obj_register_cancellable_full (handle->cancellable, + g_strdup_printf ("wpas-create-" NM_HASH_OBFUSCATE_PTR_FMT, + NM_HASH_OBFUSCATE_PTR (handle)), + TRUE); + handle->create_iface_try_count++; + g_dbus_connection_call (priv->dbus_connection, + handle->name_owner->str, + NM_WPAS_DBUS_PATH, + NM_WPAS_DBUS_INTERFACE, + "CreateInterface", + g_variant_new ("(a{sv})", &builder), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + 5000, + handle->cancellable, + _create_iface_dbus_call_create_interface_cb, + handle); +} + +static void +_create_iface_dbus_start (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + char ifname[NMP_IFNAMSIZ]; + + nm_assert (priv->name_owner); + nm_assert (!handle->cancellable); + + if (!nm_platform_if_indextoname (NM_PLATFORM_GET, handle->ifindex, ifname)) { + nm_utils_error_set (&handle->fail_on_idle_error, + NM_UTILS_ERROR_UNKNOWN, + "Cannot find interface %d", + handle->ifindex); + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: creating interface fails to find interface name for ifindex %d", + NM_HASH_OBFUSCATE_PTR (handle), + handle->ifindex); + handle->fail_on_idle_id = g_idle_add (_create_iface_fail_on_idle_cb, handle); + return; + } + + /* Our handle keeps @self alive. That means, when NetworkManager shall shut + * down, it's the responsibility of the callers to cancel the handles, + * to initiate coordinated shutdown. + * + * However, we now issue a CreateInterface call. Even if the handle gets cancelled + * (because of shutdown, or because the caller is no longer interested in the + * result), we don't want to cancel this request. Instead, we want to get + * the interface path and remove it right away. + * + * That means, the D-Bus call cannot be cancelled (because we always care about + * the result). Only the @handle can be cancelled, but parts of the handle will + * stick around to complete the task. + * + * See also handle->shutdown_handle. + */ + handle->name_owner = nm_ref_string_ref (priv->name_owner); + handle->cancellable = g_cancellable_new (); + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: creating interface (ifname \"%s\")...", + NM_HASH_OBFUSCATE_PTR (handle), + ifname); + _create_iface_dbus_call_create_interface (self, handle, ifname); +} + +static gboolean +_create_iface_fail_on_idle_cb (gpointer user_data) +{ + NMSupplMgrCreateIfaceHandle *handle = user_data; + + handle->fail_on_idle_id = 0; + + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: fail with internal error: %s", + NM_HASH_OBFUSCATE_PTR (handle), + handle->fail_on_idle_error->message); + + _create_iface_complete (handle, NULL, handle->fail_on_idle_error); + return G_SOURCE_REMOVE; +} + +NMSupplMgrCreateIfaceHandle * nm_supplicant_manager_create_interface (NMSupplicantManager *self, - const char *ifname, - NMSupplicantDriver driver) + int ifindex, + NMSupplicantDriver driver, + NMSupplicantManagerCreateInterfaceCb callback, + gpointer user_data) { NMSupplicantManagerPrivate *priv; - NMSupplicantInterface *sup_iface; + NMSupplMgrCreateIfaceHandle *handle; g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); - g_return_val_if_fail (ifname != NULL, NULL); + g_return_val_if_fail (ifindex > 0, NULL); + g_return_val_if_fail (callback, NULL); + nm_assert (nm_supplicant_driver_to_string (driver)); priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - _LOGD ("(%s): creating new supplicant interface", ifname); + handle = g_slice_new (NMSupplMgrCreateIfaceHandle); + *handle = (NMSupplMgrCreateIfaceHandle) { + .self = g_object_ref (self), + .callback = callback, + .callback_user_data = user_data, + .driver = driver, + .ifindex = ifindex, + }; + c_list_link_tail (&priv->create_iface_lst_head, &handle->create_iface_lst); + + if (!priv->dbus_connection) { + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). Fail bacause no D-Bus connection to talk to wpa_supplicant...", + NM_HASH_OBFUSCATE_PTR (handle), + ifindex, + nm_supplicant_driver_to_string (driver)); + nm_utils_error_set (&handle->fail_on_idle_error, + NM_UTILS_ERROR_UNKNOWN, + "No D-Bus connection to talk to wpa_supplicant"); + handle->fail_on_idle_id = g_idle_add (_create_iface_fail_on_idle_cb, handle); + return handle; + } + + if (!priv->name_owner) { + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). %s", + NM_HASH_OBFUSCATE_PTR (handle), + ifindex, + nm_supplicant_driver_to_string (driver), + priv->poke_name_owner_cancellable + ? "Waiting for supplicant..." + : "Poke supplicant..."); + _poke_name_owner (self); + return handle; + } - c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) { - if (nm_streq0 (nm_supplicant_interface_get_ifname (sup_iface), ifname)) - g_return_val_if_reached (NULL); + if (priv->get_capabilities_cancellable) { + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). Waiting to fetch capabilities for %s...", + NM_HASH_OBFUSCATE_PTR (handle), + ifindex, + nm_supplicant_driver_to_string (driver), + priv->name_owner->str); + return handle; } - sup_iface = nm_supplicant_interface_new (ifname, - NULL, - driver, - priv->capabilities); + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: new request interface %d (driver %s). create interface on %s...", + NM_HASH_OBFUSCATE_PTR (handle), + ifindex, + nm_supplicant_driver_to_string (driver), + priv->name_owner->str); - c_list_link_tail (&priv->supp_lst_head, &sup_iface->supp_lst); - g_object_add_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self); + _create_iface_dbus_start (self, handle); + return handle; +} - /* If we're making the supplicant take a time out for a bit, don't - * let the supplicant interface start immediately, just let it hang - * around in INIT state until we're ready to talk to the supplicant - * again. - */ - if (is_available (self)) - nm_supplicant_interface_set_supplicant_available (sup_iface, TRUE); +static void +_create_iface_proceed_all (NMSupplicantManager *self, + GError *error) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + NMSupplMgrCreateIfaceHandle *handle; + + nm_assert (error || priv->name_owner); + nm_assert (error || !priv->get_capabilities_cancellable); - return sup_iface; + if (c_list_is_empty (&priv->create_iface_lst_head)) + return; + + if (error) { + CList alt_list; + + /* we move the handles we want to proceed to a alternative list. + * That is, because we invoke callbacks to the caller, who might + * create another request right away. We don't want to proceed + * that one. */ + c_list_init (&alt_list); + c_list_splice (&alt_list, &priv->create_iface_lst_head); + + while ((handle = c_list_last_entry (&alt_list, NMSupplMgrCreateIfaceHandle, create_iface_lst))) { + /* We don't need to keep @self alive. Every handle holds a reference already. */ + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: create interface failed: %s", + NM_HASH_OBFUSCATE_PTR (handle), + error->message); + _create_iface_complete (handle, NULL, error); + } + return; + } + + /* start all the handles. This does not invoke callbacks, so the list of handles + * cannot be modified while we iterate it. */ + c_list_for_each_entry (handle, &priv->create_iface_lst_head, create_iface_lst) { + _LOGT ("create-iface["NM_HASH_OBFUSCATE_PTR_FMT"]: create interface on %s...", + NM_HASH_OBFUSCATE_PTR (handle), + priv->name_owner->str); + _create_iface_dbus_start (self, handle); + } +} + +void +nm_supplicant_manager_create_interface_cancel (NMSupplMgrCreateIfaceHandle *handle) +{ + gs_free_error GError *error = NULL; + + if (!handle) + return; + + g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (handle->self)); + g_return_if_fail (handle->callback); + nm_assert (!c_list_is_empty (&handle->create_iface_lst)); + + nm_utils_error_set_cancelled (&error, FALSE, NULL); + _create_iface_complete (handle, NULL, error); } -/** - * nm_supplicant_manager_create_interface_from_path: - * @self: the #NMSupplicantManager - * @object_path: the DBus object path for which to obtain the supplicant interface - * - * Note: the manager owns a reference to the instance and the only way to - * get the manager to release it, is by dropping all other references - * to the supplicant-interface (or destroying the manager). - * - * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL. - * Must be unrefed at the end. - * */ NMSupplicantInterface * nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, const char *object_path) { NMSupplicantManagerPrivate *priv; - NMSupplicantInterface *sup_iface; + NMSupplicantInterface *supp_iface; + nm_auto_ref_string NMRefString *iface_path = NULL; g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); - g_return_val_if_fail (object_path != NULL, NULL); + g_return_val_if_fail (object_path, NULL); priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - _LOGD ("creating new supplicant interface for dbus path %s", object_path); + iface_path = nm_ref_string_new (object_path); - c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) { - if (nm_streq0 (nm_supplicant_interface_get_object_path (sup_iface), object_path)) - g_return_val_if_reached (NULL); - } + supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path); - sup_iface = nm_supplicant_interface_new (NULL, - object_path, - NM_SUPPLICANT_DRIVER_WIRELESS, - priv->capabilities); + if (supp_iface) + return g_object_ref (supp_iface); - c_list_link_tail (&priv->supp_lst_head, &sup_iface->supp_lst); - g_object_add_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self); + supp_iface = nm_supplicant_interface_new (self, + iface_path, + 0, + NM_SUPPLICANT_DRIVER_UNKNOWN); - /* If we're making the supplicant take a time out for a bit, don't - * let the supplicant interface start immediately, just let it hang - * around in INIT state until we're ready to talk to the supplicant - * again. - */ - if (is_available (self)) - nm_supplicant_interface_set_supplicant_available (sup_iface, TRUE); + _supp_iface_add (self, iface_path, supp_iface); - return sup_iface; + return supp_iface; } +/*****************************************************************************/ + static void -update_capabilities (NMSupplicantManager *self) +_dbus_interface_removed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *signal_interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { + NMSupplicantManager *self = user_data; NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - NMSupplicantInterface *sup_iface; - const char **array; - GVariant *value; + NMSupplicantInterface *supp_iface; + const char *iface_path_str; + nm_auto_ref_string NMRefString *iface_path = NULL; + + nm_assert (nm_streq (sender_name, priv->name_owner->str)); + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) + return; + + g_variant_get (parameters, "(&o)", &iface_path_str); + + iface_path = nm_ref_string_new (iface_path_str); + + supp_iface = g_hash_table_lookup (priv->supp_ifaces, iface_path); + if (!supp_iface) + return; + + _supp_iface_remove_one (self, supp_iface, FALSE, "InterfaceRemoved signal from wpa_supplicant"); +} + +/*****************************************************************************/ + +static void +_dbus_get_capabilities_cb (GVariant *res, + GError *error, + gpointer user_data) +{ + NMSupplicantManager *self; + NMSupplicantManagerPrivate *priv; + + if (nm_utils_error_is_cancelled (error)) + return; + + self = user_data; + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + g_clear_object (&priv->get_capabilities_cancellable); /* The supplicant only advertises global capabilities if the following * commit has been applied: @@ -315,53 +935,61 @@ update_capabilities (NMSupplicantManager *self) _caps_set (priv, NM_SUPPL_CAP_TYPE_FT, NM_TERNARY_FALSE); _caps_set (priv, NM_SUPPL_CAP_TYPE_SHA384, NM_TERNARY_FALSE); _caps_set (priv, NM_SUPPL_CAP_TYPE_MESH, NM_TERNARY_FALSE); - - value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); - if (value) { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { - array = g_variant_get_strv (value, NULL); - _caps_set (priv, NM_SUPPL_CAP_TYPE_AP, NM_TERNARY_FALSE); - _caps_set (priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_FALSE); - _caps_set (priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_FALSE); - if (array) { - if (g_strv_contains (array, "ap")) _caps_set (priv, NM_SUPPL_CAP_TYPE_AP, NM_TERNARY_TRUE); - if (g_strv_contains (array, "pmf")) _caps_set (priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_TRUE); - if (g_strv_contains (array, "fils")) _caps_set (priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_TRUE); - if (g_strv_contains (array, "p2p")) _caps_set (priv, NM_SUPPL_CAP_TYPE_P2P, NM_TERNARY_TRUE); - if (g_strv_contains (array, "ft")) _caps_set (priv, NM_SUPPL_CAP_TYPE_FT, NM_TERNARY_TRUE); - if (g_strv_contains (array, "sha384")) _caps_set (priv, NM_SUPPL_CAP_TYPE_SHA384, NM_TERNARY_TRUE); - if (g_strv_contains (array, "mesh")) _caps_set (priv, NM_SUPPL_CAP_TYPE_MESH, NM_TERNARY_TRUE); - g_free (array); + _caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_FALSE); + _caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_FALSE); + + if (res) { + nm_auto_free_variant_iter GVariantIter *res_iter = NULL; + const char *res_key; + GVariant *res_val; + + g_variant_get (res, "(a{sv})", &res_iter); + while (g_variant_iter_loop (res_iter, "{&sv}", &res_key, &res_val)) { + if (nm_streq (res_key, "Capabilities")) { + if (g_variant_is_of_type (res_val, G_VARIANT_TYPE_STRING_ARRAY)) { + gs_free const char **array = NULL; + const char **a; + + array = g_variant_get_strv (res_val, NULL); + _caps_set (priv, NM_SUPPL_CAP_TYPE_AP, NM_TERNARY_FALSE); + _caps_set (priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_FALSE); + _caps_set (priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_FALSE); + if (array) { + for (a = array; *a; a++) { + if (nm_streq (*a, "ap")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_AP, NM_TERNARY_TRUE); continue; } + if (nm_streq (*a, "pmf")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_TRUE); continue; } + if (nm_streq (*a, "fils")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_TRUE); continue; } + if (nm_streq (*a, "p2p")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_P2P, NM_TERNARY_TRUE); continue; } + if (nm_streq (*a, "ft")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_FT, NM_TERNARY_TRUE); continue; } + if (nm_streq (*a, "sha384")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_SHA384, NM_TERNARY_TRUE); continue; } + if (nm_streq (*a, "mesh")) { _caps_set (priv, NM_SUPPL_CAP_TYPE_MESH, NM_TERNARY_TRUE); continue; } + } + } + } + continue; } - } - g_variant_unref (value); - } - - _caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_FALSE); - value = g_dbus_proxy_get_cached_property (priv->proxy, "EapMethods"); - if (value) { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { - array = g_variant_get_strv (value, NULL); - if (array) { - const char **a; - - for (a = array; *a; a++) { - if (g_ascii_strcasecmp (*a, "FAST") == 0) { - _caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_TRUE); - break; + if (nm_streq (res_key, "EapMethods")) { + if (g_variant_is_of_type (res_val, G_VARIANT_TYPE_STRING_ARRAY)) { + gs_free const char **array = NULL; + const char **a; + + array = g_variant_get_strv (res_val, NULL); + if (array) { + for (a = array; *a; a++) { + if (g_ascii_strcasecmp (*a, "FAST") == 0) { + _caps_set (priv, NM_SUPPL_CAP_TYPE_FAST, NM_TERNARY_TRUE); + break; + } + } } } - g_free (array); + continue; + } + if (nm_streq (res_key, "WFDIEs")) { + _caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_TRUE); + continue; } } - g_variant_unref (value); - } - - _caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_FALSE); - value = g_dbus_proxy_get_cached_property (priv->proxy, "WFDIEs"); - if (value) { - _caps_set (priv, NM_SUPPL_CAP_TYPE_WFD, NM_TERNARY_TRUE); - g_variant_unref (value); } _LOGD ("AP mode is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_AP)); @@ -374,161 +1002,307 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("EAP-FAST is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_FAST)); _LOGD ("WFD is %s", _caps_to_str (priv, NM_SUPPL_CAP_TYPE_WFD)); - c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) { - nm_supplicant_interface_set_global_capabilities (sup_iface, - priv->capabilities); - } + nm_assert (g_hash_table_size (priv->supp_ifaces) == 0); + nm_assert (c_list_is_empty (&priv->supp_lst_head)); + + _create_iface_proceed_all (self, NULL); } -static void -availability_changed (NMSupplicantManager *self, gboolean available) +/*****************************************************************************/ + +void +_nm_supplicant_manager_unregister_interface (NMSupplicantManager *self, + NMSupplicantInterface *supp_iface) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - gs_unref_ptrarray GPtrArray *sup_ifaces = NULL; - NMSupplicantInterface *sup_iface; - gsize i, n; - n = c_list_length (&priv->supp_lst_head); - if (n == 0) - return; + nm_assert (NM_IS_SUPPLICANT_INTERFACE (supp_iface)); + nm_assert (c_list_contains (&NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->supp_lst_head, &supp_iface->supp_lst)); - /* setting the supplicant as unavailable might cause the caller to unref - * the supplicant (and thus remove the instance from the list of interfaces. - * Delay that by taking an additional reference first. */ + c_list_unlink (&supp_iface->supp_lst); + if (!g_hash_table_remove (priv->supp_ifaces, nm_supplicant_interface_get_object_path (supp_iface))) + nm_assert_not_reached (); +} - sup_ifaces = g_ptr_array_new_full (n, g_object_unref); - c_list_for_each_entry (sup_iface, &priv->supp_lst_head, supp_lst) - g_ptr_array_add (sup_ifaces, g_object_ref (sup_iface)); +static void +_supp_iface_add (NMSupplicantManager *self, + NMRefString *iface_path, + NMSupplicantInterface *supp_iface) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - for (i = 0; i < n; i++) - nm_supplicant_interface_set_supplicant_available (sup_ifaces->pdata[i], available); + c_list_link_tail (&priv->supp_lst_head, &supp_iface->supp_lst); + if (!g_hash_table_insert (priv->supp_ifaces, iface_path, supp_iface)) + nm_assert_not_reached (); } static void -set_running (NMSupplicantManager *self, gboolean now_running) +_supp_iface_remove_one (NMSupplicantManager *self, + NMSupplicantInterface *supp_iface, + gboolean force_remove_from_supplicant, + const char *reason) { - NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - gboolean old_available = is_available (self); - gboolean new_available; + nm_assert (NM_IS_SUPPLICANT_MANAGER (self)); + nm_assert (NM_IS_SUPPLICANT_INTERFACE (supp_iface)); + nm_assert (c_list_contains (&NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->supp_lst_head, &supp_iface->supp_lst)); + + _nm_supplicant_interface_set_state_down (supp_iface, force_remove_from_supplicant, reason); - priv->running = now_running; - new_available = is_available (self); - if (old_available != new_available) - availability_changed (self, new_available); + nm_assert (c_list_is_empty (&supp_iface->supp_lst)); } static void -set_die_count (NMSupplicantManager *self, guint new_die_count) +_supp_iface_remove_all (NMSupplicantManager *self, + gboolean force_remove_from_supplicant, + const char *reason) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - gboolean old_available = is_available (self); - gboolean new_available; + NMSupplicantInterface *supp_iface; - priv->die_count = new_die_count; - new_available = is_available (self); - if (old_available != new_available) - availability_changed (self, new_available); + while ((supp_iface = c_list_first_entry (&priv->supp_lst_head, NMSupplicantInterface, supp_lst))) + _supp_iface_remove_one (self, supp_iface, force_remove_from_supplicant, reason); } +/*****************************************************************************/ + static gboolean -wpas_die_count_reset_cb (gpointer user_data) +_available_reset_cb (gpointer user_data) { - NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data); + NMSupplicantManager *self = user_data; NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - /* Reset the die count back to zero, which allows use of the supplicant again */ - priv->die_count_reset_id = 0; - set_die_count (self, 0); - _LOGI ("wpa_supplicant die count reset"); - return FALSE; + priv->available_reset_id = 0; + nm_assert (priv->available == NM_TERNARY_FALSE); + priv->available = NM_TERNARY_DEFAULT; + g_signal_emit (self, signals[AVAILABLE_CHANGED], 0); + return G_SOURCE_REMOVE; } +/*****************************************************************************/ + static void -name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data) +name_owner_changed (NMSupplicantManager *self, + const char *name_owner, + gboolean first_time) { - NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data); NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - char *owner; - - g_return_if_fail (proxy == priv->proxy); - - owner = g_dbus_proxy_get_name_owner (proxy); - _LOGI ("wpa_supplicant %s", owner ? "running" : "stopped"); - - if (owner) { - update_capabilities (self); - set_running (self, TRUE); - } else if (priv->running) { - /* Reschedule the die count reset timeout. Every time the supplicant - * dies we wait 10 seconds before resetting the counter. If the - * supplicant died more than twice before the timer is reset, then - * we don't try to talk to the supplicant for a while. - */ - if (priv->die_count_reset_id) - g_source_remove (priv->die_count_reset_id); - priv->die_count_reset_id = g_timeout_add_seconds (10, wpas_die_count_reset_cb, self); - set_die_count (self, priv->die_count + 1); - - if (die_count_exceeded (priv->die_count)) { - _LOGI ("wpa_supplicant die count %d; ignoring for 10 seconds", - priv->die_count); + NMTernary available; + gboolean available_changed = FALSE; + + nm_assert (!priv->get_name_owner_cancellable); + nm_assert ( !name_owner + || name_owner[0]); + nm_assert ( ( first_time + && !priv->name_owner) + || ( !first_time + && (!!priv->name_owner) != (!!name_owner))); + + if (first_time) { + _LOGD ("wpa_supplicant name owner %s%s%s (%srunning)", + NM_PRINT_FMT_QUOTE_STRING (name_owner), + name_owner ? "" : "not "); + } else { + _LOGD ("wpa_supplicant name owner \"%s\" %s (%srunning)", + name_owner ?: priv->name_owner->str, + name_owner ? "disappeared" : "appeared", + name_owner ? "" : "not "); + } + + nm_ref_string_unref (priv->name_owner); + priv->name_owner = nm_ref_string_new (name_owner); + + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->interface_removed_id); + + if (name_owner) { + if (nm_clear_g_source (&priv->poke_name_owner_timeout_id)) + _LOGT ("poke service \"%s\" completed with name owner change", NM_WPAS_DBUS_SERVICE); + nm_clear_g_cancellable (&priv->poke_name_owner_cancellable); + } + + nm_clear_g_cancellable (&priv->get_capabilities_cancellable); + + priv->capabilities = NM_SUPPL_CAP_MASK_NONE; + if (priv->name_owner) { + priv->get_capabilities_cancellable = g_cancellable_new (); + nm_dbus_connection_call_get_all (priv->dbus_connection, + priv->name_owner->str, + NM_WPAS_DBUS_PATH, + NM_WPAS_DBUS_INTERFACE, + 5000, + priv->get_capabilities_cancellable, + _dbus_get_capabilities_cb, + self); + priv->interface_removed_id = g_dbus_connection_signal_subscribe (priv->dbus_connection, + priv->name_owner->str, + NM_WPAS_DBUS_INTERFACE, + "InterfaceRemoved", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_interface_removed_cb, + self, + NULL); + } + + /* if supplicant is running (has a name owner), we may use it. + * If this is the first time, and supplicant is not running, we + * may also use it (and assume that we probably could D-Bus activate + * it). + * + * Otherwise, somebody else stopped supplicant. It's no longer useable to + * us and we block auto starting it. The user has to start the service... + * + * Actually, below we reset the hard block after a short timeout. This + * causes the caller to notify that supplicant may now by around and + * retry to D-Bus activate it. */ + if (priv->name_owner) + available = NM_TERNARY_TRUE; + else if (first_time) + available = NM_TERNARY_DEFAULT; + else + available = NM_TERNARY_FALSE; + + if (priv->available != available) { + priv->available = available; + _LOGD ("supplicant is now %savailable", + available == FALSE + ? "not " + : ( available == TRUE + ? "" + : "maybe ")); + available_changed = TRUE; + + nm_clear_g_source (&priv->available_reset_id); + if (available == NM_TERNARY_FALSE) { + /* reset the availability from a hard "no" to a "maybe" in a bit. */ + priv->available_reset_id = g_timeout_add_seconds (60, + _available_reset_cb, + self); } + } - priv->capabilities = NM_SUPPL_CAP_MASK_NONE; + _supp_iface_remove_all (self, TRUE, "name-owner changed"); - set_running (self, FALSE); + if (!priv->name_owner) { + if (priv->poke_name_owner_timeout_id) { + /* we are still poking for the service to start. Don't cancel + * the pending create requests just yet. */ + } else { + gs_free_error GError *local_error = NULL; + + /* When we loose the name owner, we fail all pending creation requests. */ + nm_utils_error_set (&local_error, + NM_UTILS_ERROR_UNKNOWN, + "Name owner lost"); + _create_iface_proceed_all (self, local_error); + } + } else { + /* We got a name-owner, but we don't do anything. Instead let + * _dbus_get_capabilities_cb() complete and kick of the create-iface + * handles. + * + * Note that before the first name-owner change, all create-iface + * requests fail right away. So we don't have to handle them here + * (by starting to poke the service). */ } - g_free (owner); + if (available_changed) + g_signal_emit (self, signals[AVAILABLE_CHANGED], 0); } static void -on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data) +name_owner_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { - NMSupplicantManager *self; - NMSupplicantManagerPrivate *priv; - GError *error = NULL; - GDBusProxy *proxy; + gs_unref_object NMSupplicantManager *self = g_object_ref (user_data); + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + const char *name_owner; - proxy = g_dbus_proxy_new_for_bus_finish (result, &error); - if (!proxy) { - _LOGW ("failed to acquire wpa_supplicant proxy: Wi-Fi and 802.1x will not be available (%s)", - error->message); - g_clear_error (&error); + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) + return; + + if (priv->get_name_owner_cancellable) + return; + + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &name_owner); + + name_owner = nm_str_not_empty (name_owner); + + if (nm_streq0 (name_owner, nm_ref_string_get_str (priv->name_owner))) return; + + if ( name_owner + && priv->name_owner) { + /* odd, we directly switch from one name owner to the next. Can't allow that. + * First clear the name owner before resetting. */ + name_owner_changed (self, NULL, FALSE); } + name_owner_changed (user_data, name_owner, FALSE); +} + +static void +get_name_owner_cb (const char *name_owner, + GError *error, + gpointer user_data) +{ + NMSupplicantManager *self = user_data; + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + if ( !name_owner + && nm_utils_error_is_cancelled (error)) + return; - self = NM_SUPPLICANT_MANAGER (user_data); + self = user_data; priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - priv->proxy = proxy; - g_signal_connect (priv->proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self); - name_owner_cb (priv->proxy, NULL, self); + g_clear_object (&priv->get_name_owner_cancellable); + + name_owner_changed (self, nm_str_not_empty (name_owner), TRUE); } /*****************************************************************************/ -NM_DEFINE_SINGLETON_GETTER (NMSupplicantManager, nm_supplicant_manager_get, NM_TYPE_SUPPLICANT_MANAGER); - static void nm_supplicant_manager_init (NMSupplicantManager *self) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); nm_assert (priv->capabilities == NM_SUPPL_CAP_MASK_NONE); + nm_assert (priv->available == NM_TERNARY_FALSE); + priv->supp_ifaces = g_hash_table_new (nm_direct_hash, NULL); c_list_init (&priv->supp_lst_head); + c_list_init (&priv->create_iface_lst_head); + + priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); + + if (!priv->dbus_connection) { + _LOGI ("no D-Bus connection to talk to wpa_supplicant"); + return; + } - priv->cancellable = g_cancellable_new (); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - NM_WPAS_DBUS_SERVICE, - NM_WPAS_DBUS_PATH, - NM_WPAS_DBUS_INTERFACE, - priv->cancellable, - (GAsyncReadyCallback) on_proxy_acquired, - self); + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + NM_WPAS_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + priv->get_name_owner_cancellable = g_cancellable_new (); + nm_dbus_connection_call_get_name_owner (priv->dbus_connection, + NM_WPAS_DBUS_SERVICE, + -1, + priv->get_name_owner_cancellable, + get_name_owner_cb, + self); } static void @@ -536,20 +1310,32 @@ dispose (GObject *object) { NMSupplicantManager *self = (NMSupplicantManager *) object; NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - NMSupplicantInterface *sup_iface; - nm_clear_g_source (&priv->die_count_reset_id); + _supp_iface_remove_all (self, TRUE, "NMSupplicantManager is disposing"); - nm_clear_g_cancellable (&priv->cancellable); + nm_assert (c_list_is_empty (&priv->create_iface_lst_head)); - while ((sup_iface = c_list_first_entry (&priv->supp_lst_head, NMSupplicantInterface, supp_lst))) { - c_list_unlink (&sup_iface->supp_lst); - g_object_remove_toggle_ref (G_OBJECT (sup_iface), _sup_iface_last_ref, self); - } + nm_clear_g_source (&priv->available_reset_id); + + priv->available = NM_TERNARY_FALSE; + nm_clear_pointer (&priv->name_owner, nm_ref_string_unref); + + nm_clear_g_source (&priv->poke_name_owner_timeout_id); + nm_clear_g_cancellable (&priv->poke_name_owner_cancellable); - g_clear_object (&priv->proxy); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->interface_removed_id); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->name_owner_changed_id); + + nm_clear_g_cancellable (&priv->get_name_owner_cancellable); + nm_clear_g_cancellable (&priv->get_capabilities_cancellable); G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object); + + g_clear_object (&priv->dbus_connection); + + nm_clear_pointer (&priv->supp_ifaces, g_hash_table_destroy); } static void @@ -558,5 +1344,11 @@ nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = dispose; -} + signals[AVAILABLE_CHANGED] = + g_signal_new (NM_SUPPLICANT_MANAGER_AVAILABLE_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); +} diff --git a/src/supplicant/nm-supplicant-manager.h b/src/supplicant/nm-supplicant-manager.h index 18ca53b6f1..b2b814aa50 100644 --- a/src/supplicant/nm-supplicant-manager.h +++ b/src/supplicant/nm-supplicant-manager.h @@ -17,19 +17,49 @@ #define NM_IS_SUPPLICANT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_MANAGER)) #define NM_SUPPLICANT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass)) +#define NM_SUPPLICANT_MANAGER_AVAILABLE_CHANGED "available-changed" + typedef struct _NMSupplicantManagerClass NMSupplicantManagerClass; GType nm_supplicant_manager_get_type (void); NMSupplicantManager *nm_supplicant_manager_get (void); +NMTernary nm_supplicant_manager_is_available (NMSupplicantManager *self); + +GDBusConnection *nm_supplicant_manager_get_dbus_connection (NMSupplicantManager *self); +NMRefString *nm_supplicant_manager_get_dbus_name_owner (NMSupplicantManager *self); +NMSupplCapMask nm_supplicant_manager_get_global_capabilities (NMSupplicantManager *self); + void nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self, GBytes *wfd_ies); -NMSupplicantInterface *nm_supplicant_manager_create_interface (NMSupplicantManager *mgr, - const char *ifname, - NMSupplicantDriver driver); +typedef struct _NMSupplMgrCreateIfaceHandle NMSupplMgrCreateIfaceHandle; + +typedef void (*NMSupplicantManagerCreateInterfaceCb) (NMSupplicantManager *self, + NMSupplMgrCreateIfaceHandle *handle, + NMSupplicantInterface *iface, + GError *error, + gpointer user_data); + +NMSupplMgrCreateIfaceHandle *nm_supplicant_manager_create_interface (NMSupplicantManager *self, + int ifindex, + NMSupplicantDriver driver, + NMSupplicantManagerCreateInterfaceCb callback, + gpointer user_data); + +void nm_supplicant_manager_create_interface_cancel (NMSupplMgrCreateIfaceHandle *handle); + NMSupplicantInterface *nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, const char *object_path); +/*****************************************************************************/ + +void _nm_supplicant_manager_unregister_interface (NMSupplicantManager *self, + NMSupplicantInterface *supp_iface); + +void _nm_supplicant_manager_dbus_call_remove_interface (NMSupplicantManager *self, + const char *name_owner, + const char *iface_path); + #endif /* __NETWORKMANAGER_SUPPLICANT_MANAGER_H__ */ diff --git a/src/supplicant/nm-supplicant-types.h b/src/supplicant/nm-supplicant-types.h index 17fbc0c009..bad52c345a 100644 --- a/src/supplicant/nm-supplicant-types.h +++ b/src/supplicant/nm-supplicant-types.h @@ -6,6 +6,8 @@ #ifndef __NETWORKMANAGER_SUPPLICANT_TYPES_H__ #define __NETWORKMANAGER_SUPPLICANT_TYPES_H__ +#include "c-list/src/c-list.h" + #define NM_WPAS_DBUS_SERVICE "fi.w1.wpa_supplicant1" #define NM_WPAS_DBUS_PATH "/fi/w1/wpa_supplicant1" #define NM_WPAS_DBUS_INTERFACE "fi.w1.wpa_supplicant1" @@ -138,4 +140,65 @@ const char *nm_supplicant_driver_to_string (NMSupplicantDriver driver); #define NM_SUPPLICANT_ERROR (nm_supplicant_error_quark ()) GQuark nm_supplicant_error_quark (void); +typedef struct _NMSupplicantBssInfo { + NMRefString *bss_path; + + NMSupplicantInterface *_self; + CList _bss_lst; + GCancellable *_init_cancellable; + + GBytes *ssid; + + gint64 last_seen_msec; + + NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */ + NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */ + + guint32 frequency; + + guint32 max_rate; + + guint8 signal_percent; + + guint8 bssid[6 /* ETH_ALEN */]; + + NM80211ApFlags ap_flags:5; + + NM80211Mode mode:4; + + bool bssid_valid:1; + + bool metered:1; + + bool _bss_dirty:1; + +} NMSupplicantBssInfo; + +typedef struct _NMSupplicantPeerInfo{ + NMRefString *peer_path; + + CList _peer_lst; + NMSupplicantInterface *_self; + GCancellable *_init_cancellable; + + char *device_name; + char *manufacturer; + char *model; + char *model_number; + char *serial; + + GBytes *ies; + + gint64 last_seen_msec; + + guint8 address[6 /* ETH_ALEN */]; + + gint8 signal_percent; + + bool address_valid:1; + + bool _peer_dirty:1; + +} NMSupplicantPeerInfo; + #endif /* NM_SUPPLICANT_TYPES_H */ |