diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index eaf504cc4..022c37cbb 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -75,10 +75,12 @@ typedef struct { GCancellable * cancellable; NMDeviceWifiCapabilities capabilities; NMActRequestGetSecretsCallId *wifi_secrets_id; + guint periodic_scan_id; bool enabled:1; bool can_scan:1; bool can_connect:1; bool scanning:1; + bool scan_requested:1; } NMDeviceIwdPrivate; struct _NMDeviceIwd { @@ -101,6 +103,9 @@ G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE) /*****************************************************************************/ +static void schedule_periodic_scan (NMDeviceIwd *self, + NMDeviceState current_state); + static void _ap_dump (NMDeviceIwd *self, NMLogLevel log_level, @@ -863,6 +868,33 @@ check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic) return prohibited; } +static void +scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv; + gs_free_error GError *error = NULL; + + if ( !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, + G_VARIANT_TYPE ("()"), &error) + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + priv->scan_requested = FALSE; + + /* On success, priv->scanning becomes true right before or right + * after this callback, so the next automatic scan will be + * scheduled when priv->scanning goes back to false. On error, + * schedule a retry now. + */ + if (error && !priv->scanning) { + NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); + + schedule_periodic_scan (self, state); + } +} + static void dbus_request_scan_cb (NMDevice *device, GDBusMethodInvocation *context, @@ -912,8 +944,14 @@ dbus_request_scan_cb (NMDevice *device, } } - g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + if (!priv->scanning && !priv->scan_requested) { + g_dbus_proxy_call (priv->dbus_proxy, "Scan", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, + priv->cancellable, scan_cb, self); + priv->scan_requested = TRUE; + } + g_dbus_method_invocation_return_value (context, NULL); } @@ -1461,6 +1499,45 @@ get_configured_mtu (NMDevice *device, gboolean *out_is_user_config) return mtu; } +static gboolean +periodic_scan_timeout_cb (gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + priv->periodic_scan_id = 0; + + if (priv->scanning || priv->scan_requested) + return FALSE; + + g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, + priv->cancellable, scan_cb, self); + priv->scan_requested = TRUE; + + return FALSE; +} + +static void +schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + guint interval; + + if (current_state <= NM_DEVICE_STATE_UNAVAILABLE) + return; + + if (current_state == NM_DEVICE_STATE_DISCONNECTED) + interval = 10; + else + interval = 20; + + nm_clear_g_source (&priv->periodic_scan_id); + priv->periodic_scan_id = g_timeout_add_seconds (interval, + periodic_scan_timeout_cb, + self); +} + static void device_state_changed (NMDevice *device, NMDeviceState new_state, @@ -1470,10 +1547,13 @@ device_state_changed (NMDevice *device, NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) + if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { remove_all_aps (self); - else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) + nm_clear_g_source (&priv->periodic_scan_id); + } else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) { update_aps (self); + schedule_periodic_scan (self, new_state); + } switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: @@ -1710,6 +1790,7 @@ static void scanning_changed (NMDeviceIwd *self, gboolean new_scanning) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); if (new_scanning == priv->scanning) return; @@ -1718,8 +1799,12 @@ scanning_changed (NMDeviceIwd *self, gboolean new_scanning) _notify (self, PROP_SCANNING); - if (!priv->scanning) + if (!priv->scanning) { update_aps (self); + + if (!priv->scan_requested) + schedule_periodic_scan (self, state); + } } static void @@ -1750,7 +1835,7 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); GDBusInterface *interface; - gs_unref_variant GVariant *value = NULL; + GVariant *value; if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object)) return; @@ -1779,6 +1864,12 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Scanning"); priv->scanning = g_variant_get_boolean (value); + g_variant_unref (value); + priv->scan_requested = FALSE; + + value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "State"); + state_changed (self, g_variant_get_string (value, NULL)); + g_variant_unref (value); g_signal_connect (priv->dbus_proxy, "g-properties-changed", G_CALLBACK (properties_changed), self); @@ -1861,6 +1952,8 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->cancellable); + nm_clear_g_source (&priv->periodic_scan_id); + wifi_secrets_cancel (self); cleanup_association_attempt (self, TRUE); diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index 4eda5ebac..21f0a7555 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -39,6 +39,7 @@ typedef struct { } KnownNetworkData; typedef struct { + NMManager *nm_manager; GCancellable *cancellable; gboolean running; GDBusObjectManager *object_manager; @@ -97,7 +98,6 @@ psk_agent_dbus_method_cb (GDBusConnection *connection, gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL; gs_unref_variant GVariant *value = NULL; gint ifindex; - NMManager *manager; NMDevice *device; const gchar *psk; @@ -139,9 +139,7 @@ psk_agent_dbus_method_cb (GDBusConnection *connection, goto return_error; } - manager = nm_manager_get (); - - device = nm_manager_get_device_by_ifindex (manager, ifindex); + device = nm_manager_get_device_by_ifindex (priv->nm_manager, ifindex); if (!NM_IS_DEVICE_IWD (device)) { _LOGE ("IWD device named %s is not a Wifi device in IWD Agent request", ifname); @@ -271,7 +269,6 @@ set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface, const char *ifname; gint ifindex; NMDevice *device; - NMManager *manager; if (!priv->running) return; @@ -301,9 +298,7 @@ set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface, return; } - manager = nm_manager_get (); - - device = nm_manager_get_device_by_ifindex (manager, ifindex); + device = nm_manager_get_device_by_ifindex (priv->nm_manager, ifindex); if (!NM_IS_DEVICE_IWD (device)) { _LOGE ("IWD device named %s is not a Wifi device", ifname); return; @@ -456,6 +451,8 @@ update_known_networks (NMIwdManager *self) g_object_unref (known_networks_if); } +static void prepare_object_manager (NMIwdManager *self); + static void name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { @@ -466,27 +463,18 @@ name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data) nm_assert (object_manager == priv->object_manager); if (_om_has_name_owner (object_manager)) { - GList *objects, *iter; - - priv->running = true; - - objects = g_dbus_object_manager_get_objects (object_manager); - for (iter = objects; iter; iter = iter->next) - object_added (self, G_DBUS_OBJECT (iter->data)); - - g_list_free_full (objects, g_object_unref); - - if (priv->agent_id) - register_agent (self); - - update_known_networks (self); + g_signal_handlers_disconnect_by_data (object_manager, self); + g_clear_object (&priv->object_manager); + prepare_object_manager (self); } else { - NMManager *manager = nm_manager_get (); const GSList *devices, *iter; + if (!priv->running) + return; + priv->running = false; - devices = nm_manager_get_devices (manager); + devices = nm_manager_get_devices (priv->nm_manager); for (iter = devices; iter; iter = iter->next) { NMDevice *device = NM_DEVICE (iter->data); @@ -554,9 +542,6 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) GError *error = NULL; GDBusObjectManager *object_manager; GDBusConnection *connection; - NMManager *manager = nm_manager_get (); - - g_clear_object (&priv->cancellable); object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error); if (object_manager == NULL) { @@ -568,10 +553,6 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) priv->object_manager = object_manager; - g_signal_connect (priv->object_manager, "interface-added", - G_CALLBACK (interface_added), self); - g_signal_connect (priv->object_manager, "interface-removed", - G_CALLBACK (interface_removed), self); g_signal_connect (priv->object_manager, "notify::name-owner", G_CALLBACK (name_owner_changed), self); @@ -587,10 +568,27 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) g_clear_error (&error); } - name_owner_changed (G_OBJECT (object_manager), NULL, self); + if (_om_has_name_owner (object_manager)) { + GList *objects, *iter; - g_signal_connect (manager, "device-added", - G_CALLBACK (device_added), self); + priv->running = true; + + g_signal_connect (priv->object_manager, "interface-added", + G_CALLBACK (interface_added), self); + g_signal_connect (priv->object_manager, "interface-removed", + G_CALLBACK (interface_removed), self); + + objects = g_dbus_object_manager_get_objects (object_manager); + for (iter = objects; iter; iter = iter->next) + object_added (self, G_DBUS_OBJECT (iter->data)); + + g_list_free_full (objects, g_object_unref); + + if (priv->agent_id) + register_agent (self); + + update_known_networks (self); + } } static void @@ -649,7 +647,12 @@ nm_iwd_manager_init (NMIwdManager *self) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + priv->nm_manager = g_object_ref (nm_manager_get ()); + g_signal_connect (priv->nm_manager, "device-added", + G_CALLBACK (device_added), self); + priv->cancellable = g_cancellable_new (); + prepare_object_manager (self); } @@ -684,6 +687,11 @@ dispose (GObject *object) g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free); priv->known_networks = NULL; + if (priv->nm_manager) { + g_signal_handlers_disconnect_by_data (priv->nm_manager, self); + g_clear_object (&priv->nm_manager); + } + G_OBJECT_CLASS (nm_iwd_manager_parent_class)->dispose (object); }