From 0119fa8c3d98ef9704051d936d573147eab2610d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 23 Apr 2020 14:23:23 +0200 Subject: [PATCH 1/4] shared: add nm_utils_invoke_on_timeout() Add nm_utils_invoke_on_timeout() beside nm_utils_invoke_on_idle(). They are fundamentally similar, except one schedules an idle handler and the other a timeout. Also, use the current g_main_context_get_thread_default() as context instead of the singleton instance. That is a change in behavior, but the only caller of nm_utils_invoke_on_idle() is the daemon, which doesn't use different main contexts. Anyway, to avoid anybody being tripped up by this also change the order of arguments. It anyway seems nicer to first pass the cancellable, and the callback and user data as last arguments. It's more in line with glib's asynchronous methods. Also, in the unlikely case that the cancellable is already cancelled from the start, always schedule an idle action to complete fast. (cherry picked from commit cd5157a0c38adb20eb290a145888703b3d09dd4f) --- shared/nm-glib-aux/nm-shared-utils.c | 103 ++++++++++++++++------ shared/nm-glib-aux/nm-shared-utils.h | 13 ++- src/devices/bluetooth/nm-bluez-manager.c | 6 +- src/devices/ovs/nm-device-ovs-interface.c | 2 +- src/devices/wifi/nm-device-iwd.c | 2 +- src/devices/wifi/nm-device-wifi.c | 2 +- src/devices/wwan/nm-modem-broadband.c | 6 +- src/devices/wwan/nm-modem-ofono.c | 6 +- src/platform/nm-linux-platform.c | 8 +- 9 files changed, 100 insertions(+), 48 deletions(-) diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index c06399dda8..61e3e26716 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -3655,9 +3655,9 @@ _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) typedef struct { gpointer callback_user_data; GCancellable *cancellable; + GSource *source; NMUtilsInvokeOnIdleCallback callback; gulong cancelled_id; - guint idle_id; } InvokeOnIdleData; static gboolean @@ -3665,12 +3665,13 @@ _nm_utils_invoke_on_idle_cb_idle (gpointer user_data) { InvokeOnIdleData *data = user_data; - data->idle_id = 0; nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); data->callback (data->callback_user_data, data->cancellable); + nm_g_object_unref (data->cancellable); - g_slice_free (InvokeOnIdleData, data); + g_source_destroy (data->source); + nm_g_slice_free (data); return G_SOURCE_REMOVE; } @@ -3680,41 +3681,87 @@ _nm_utils_invoke_on_idle_cb_cancelled (GCancellable *cancellable, { /* on cancellation, we invoke the callback synchronously. */ nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); - nm_clear_g_source (&data->idle_id); + nm_clear_g_source_inst (&data->source); data->callback (data->callback_user_data, data->cancellable); nm_g_object_unref (data->cancellable); - g_slice_free (InvokeOnIdleData, data); + nm_g_slice_free (data); } -void -nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data, - GCancellable *cancellable) +static void +_nm_utils_invoke_on_idle_start (gboolean use_timeout, + guint timeout_msec, + GCancellable *cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data) { InvokeOnIdleData *data; + GSource *source; g_return_if_fail (callback); data = g_slice_new (InvokeOnIdleData); - data->callback = callback; - data->callback_user_data = callback_user_data; - data->cancellable = nm_g_object_ref (cancellable); - if ( cancellable - && !g_cancellable_is_cancelled (cancellable)) { - /* if we are passed a non-cancelled cancellable, we register to the "cancelled" - * signal an invoke the callback synchronously (from the signal handler). - * - * We don't do that, - * - if the cancellable is already cancelled (because we don't want to invoke - * the callback synchronously from the caller). - * - if we have no cancellable at hand. */ - data->cancelled_id = g_signal_connect (cancellable, - "cancelled", - G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled), - data); - } else - data->cancelled_id = 0; - data->idle_id = g_idle_add (_nm_utils_invoke_on_idle_cb_idle, data); + *data = (InvokeOnIdleData) { + .callback = callback, + .callback_user_data = callback_user_data, + .cancellable = nm_g_object_ref (cancellable), + .cancelled_id = 0, + }; + + if (cancellable) { + if (g_cancellable_is_cancelled (cancellable)) { + /* the cancellable is already cancelled. We ignore the timeout + * and always schedule an idle action. */ + use_timeout = FALSE; + } else { + /* if we are passed a non-cancelled cancellable, we register to the "cancelled" + * signal an invoke the callback synchronously (from the signal handler). + * + * We don't do that, + * - if the cancellable is already cancelled (because we don't want to invoke + * the callback synchronously from the caller). + * - if we have no cancellable at hand. */ + data->cancelled_id = g_signal_connect (cancellable, + "cancelled", + G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled), + data); + } + } + + if (use_timeout) { + source = nm_g_timeout_source_new (timeout_msec, + G_PRIORITY_DEFAULT, + _nm_utils_invoke_on_idle_cb_idle, + data, + NULL); + } else { + source = nm_g_idle_source_new (G_PRIORITY_DEFAULT, + _nm_utils_invoke_on_idle_cb_idle, + data, + NULL); + } + + /* use the current thread default context. */ + g_source_attach (source, + g_main_context_get_thread_default ()); + + data->source = source; +} + +void +nm_utils_invoke_on_idle (GCancellable *cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data) +{ + _nm_utils_invoke_on_idle_start (FALSE, 0, cancellable, callback, callback_user_data); +} + +void +nm_utils_invoke_on_timeout (guint timeout_msec, + GCancellable *cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data) +{ + _nm_utils_invoke_on_idle_start (TRUE, timeout_msec, cancellable, callback, callback_user_data); } /*****************************************************************************/ diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index 38b100416d..9200f80fdf 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1629,12 +1629,17 @@ void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...); /*****************************************************************************/ -typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer callback_user_data, +typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer user_data, GCancellable *cancellable); -void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data, - GCancellable *cancellable); +void nm_utils_invoke_on_idle (GCancellable *cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data); + +void nm_utils_invoke_on_timeout (guint timeout_msec, + GCancellable *cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data); /*****************************************************************************/ diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index 6ff96c323b..0bbac4c435 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -1251,9 +1251,9 @@ _network_server_unregister_bridge (NMBluezManager *self, if (r_req_data) { nm_clear_g_cancellable (&r_req_data->int_cancellable); - nm_utils_invoke_on_idle (_network_server_unregister_bridge_complete_on_idle_cb, - nm_utils_user_data_pack (r_req_data, g_strdup (reason)), - r_req_data->ext_cancellable); + nm_utils_invoke_on_idle (r_req_data->ext_cancellable, + _network_server_unregister_bridge_complete_on_idle_cb, + nm_utils_user_data_pack (r_req_data, g_strdup (reason))); } _nm_device_bridge_notify_unregister_bt_nap (device, reason); diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index 951b578892..10f9fa9431 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -310,7 +310,7 @@ deactivate_async (NMDevice *device, nm_device_get_iface (device))) { _LOGT (LOGD_CORE, "deactivate: link not present, proceeding"); nm_device_update_from_platform_link (NM_DEVICE (self), NULL); - nm_utils_invoke_on_idle (deactivate_cb_on_idle, data, cancellable); + nm_utils_invoke_on_idle (cancellable, deactivate_cb_on_idle, data); return; } diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 3c1d5b2298..3dccd1cb94 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -507,7 +507,7 @@ deactivate_async (NMDevice *device, user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); if (!priv->dbus_obj) { - nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + nm_utils_invoke_on_idle (cancellable, disconnect_cb_on_idle, user_data); return; } diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 266a842514..40faa13470 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -684,7 +684,7 @@ deactivate_async (NMDevice *device, user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); if (!priv->sup_iface) { - nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + nm_utils_invoke_on_idle (cancellable, disconnect_cb_on_idle, user_data); return; } diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index f89ec86c9f..3fdc5ac54b 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -1254,9 +1254,9 @@ disconnect (NMModem *modem, /* Already cancelled or no simple-iface? We are done. */ if ( !ctx->self->_priv.simple_iface || g_cancellable_is_cancelled (cancellable)) { - nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, - ctx, - cancellable); + nm_utils_invoke_on_idle (cancellable, + disconnect_context_complete_on_idle, + ctx); return; } diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index 2fc44881a9..96b08e7cff 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -211,9 +211,9 @@ disconnect (NMModem *modem, if ( state != NM_MODEM_STATE_CONNECTED || g_cancellable_is_cancelled (cancellable)) { - nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, - ctx, - cancellable); + nm_utils_invoke_on_idle (cancellable, + disconnect_context_complete_on_idle, + ctx); return; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f7249bb71e..014cca71da 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -5131,9 +5131,9 @@ sysctl_set_async (NMPlatform *platform, callback, data, error); - nm_utils_invoke_on_idle (sysctl_set_async_return_idle, - packed, - cancellable); + nm_utils_invoke_on_idle (cancellable, + sysctl_set_async_return_idle, + packed); return; } } else @@ -7390,7 +7390,7 @@ out_idle: g_steal_pointer (&error), callback, data); - nm_utils_invoke_on_idle (sriov_idle_cb, packed, cancellable); + nm_utils_invoke_on_idle (cancellable, sriov_idle_cb, packed); } } From 455d42c3bd0517f5d0a2e8fe87a08ad119ca8e8f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 23 Apr 2020 14:28:27 +0200 Subject: [PATCH 2/4] cli: repeatedly trigger Wi-Fi scans while waiting for scan result NetworkManager will reject scan requests, if it is currently scanning. That is very wrong. Even if NetworkManager wants to ratelimit scan requests or not scan at critical moments, it should never reject a request, but remember and start scanning as soon as it can. That should be fixed. But regardless, also nmcli can do better. If you issue $ nmcli device wifi list --rescan yes once, it works as expected. If you issue it again right after, the scan request of nmcli will be rejected. But nmcli cannot just merely complete and print the result. Instead, it will wait in the hope that a scan result will be present soon. But if the request was simply rejected, then the result will never come, and nmcli hangs for the 15 seconds timeout. Instead, repeatedly re-trigger scan requests, in the hope that as soon as possible we will be ready. (cherry picked from commit 27e2d51abc52e203d22d63398558e4a0edd8b019) --- clients/cli/devices.c | 63 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 4ea4e772e4..7031b43756 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -2978,6 +2978,24 @@ wifi_last_scan_updated (GObject *gobject, GParamSpec *pspec, gpointer user_data) wifi_list_finish (user_data, FALSE); } +static void wifi_list_rescan_cb (GObject *source_object, GAsyncResult *res, gpointer user_data); + +static void +wifi_list_rescan_retry_cb (gpointer user_data, + GCancellable *cancellable) +{ + WifiListData *wifi_list_data; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + wifi_list_data = user_data; + nm_device_wifi_request_scan_async (wifi_list_data->wifi, + wifi_list_data->scan_cancellable, + wifi_list_rescan_cb, + wifi_list_data); +} + static void wifi_list_rescan_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { @@ -2985,22 +3003,47 @@ wifi_list_rescan_cb (GObject *source_object, GAsyncResult *res, gpointer user_da gs_free_error GError *error = NULL; WifiListData *wifi_list_data; gboolean force_finished; + gboolean done; nm_device_wifi_request_scan_finish (wifi, res, &error); if (nm_utils_error_is_cancelled (error)) return; - if (g_error_matches (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED)) { - /* This likely means that scanning is already in progress. There's - * a good chance we'll get updated results soon; wait for them. */ - force_finished = FALSE; - } else if (error) - force_finished = TRUE; - else - force_finished = FALSE; - wifi_list_data = user_data; - g_clear_object (&wifi_list_data->scan_cancellable); + + if (g_error_matches (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED)) { + if (nm_device_get_state (NM_DEVICE (wifi)) < NM_DEVICE_STATE_DISCONNECTED) { + /* the device is either unmanaged or unavailable. + * + * If it's unmanaged, we don't expect any scan result and are done. + * If it's unavailable, that usually means that we wait for wpa_supplicant + * to start. In that case, also quit (without scan results). */ + force_finished = TRUE; + done = TRUE; + } else { + /* This likely means that scanning is already in progress. There's + * a good chance we'll get updated results soon; wait for them. + * + * But also, NetworkManager ratelimits (and rejects requests). That + * means, possibly we were just ratelimited, so waiting will not lead + * to a new scan result. Instead, repeatedly ask new scans... */ + nm_utils_invoke_on_timeout (1000, + wifi_list_data->scan_cancellable, + wifi_list_rescan_retry_cb, + wifi_list_data); + force_finished = FALSE; + done = FALSE; + } + } else if (error) { + force_finished = TRUE; + done = TRUE; + } else { + force_finished = FALSE; + done = TRUE; + } + + if (done) + g_clear_object (&wifi_list_data->scan_cancellable); wifi_list_finish (wifi_list_data, force_finished); } From 6d0ea83949c144b25926f4f72ebea76e9ea3382b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 23 Apr 2020 19:43:05 +0200 Subject: [PATCH 3/4] wifi: add more trace logging to supplicant interface (cherry picked from commit 8ecc325f29f5cba83daf6fca0003c7e13bafba31) --- src/supplicant/nm-supplicant-interface.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index a02f540256..bfee298445 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -1123,7 +1123,7 @@ set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state) if (new_state == priv->state) return; - _LOGT ("set state \"%s\" (was \"%s\")", + _LOGT ("state: set state \"%s\" (was \"%s\")", nm_supplicant_interface_state_to_string (new_state), nm_supplicant_interface_state_to_string (priv->state)); @@ -1789,8 +1789,12 @@ _properties_changed_main (NMSupplicantInterface *self, g_variant_unref (v_v); } - if (nm_g_variant_lookup (properties, "Scanning", "b", &v_b)) - priv->scanning_property = v_b; + if (nm_g_variant_lookup (properties, "Scanning", "b", &v_b)) { + if (priv->scanning_property != (!!v_b)) { + _LOGT ("scanning: %s (plain property)", v_b ? "yes" : "no"); + priv->scanning_property = v_b; + } + } if (nm_g_variant_lookup (properties, "Ifname", "&s", &v_s)) { if (nm_utils_strdup_reset (&priv->ifname, v_s)) @@ -1816,8 +1820,13 @@ _properties_changed_main (NMSupplicantInterface *self, state = wpas_state_string_to_enum (v_s); if (state == NM_SUPPLICANT_INTERFACE_STATE_INVALID) - _LOGT ("ignore unknown supplicant state '%s'", v_s); + _LOGT ("state: ignore unknown supplicant state '%s' (is %s, plain property)", + v_s, + nm_supplicant_interface_state_to_string (priv->supp_state)); else if (priv->supp_state != state) { + _LOGT ("state: %s (was %s, plain property)", + nm_supplicant_interface_state_to_string (state), + nm_supplicant_interface_state_to_string (priv->supp_state)); priv->supp_state = state; if (priv->state > NM_SUPPLICANT_INTERFACE_STATE_STARTING) { /* Only transition to actual wpa_supplicant interface states (ie, From 2eb398648f26c6284070ecb4934368813c005de4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Apr 2020 16:42:13 +0200 Subject: [PATCH 4/4] wifi: add callback to nm_supplicant_interface_request_scan() While we request a scan, we are not yet actually scanning. That means, the supplicant's "scanning" property will only change to TRUE a while after we initiate the scan. It may even never happen. We thus need to handle that the request is currently pending and react when the request completes. (cherry picked from commit 16c1869476106859b684151eb1b101c24cff3451) --- src/devices/wifi/nm-device-wifi.c | 6 +- src/supplicant/nm-supplicant-interface.c | 92 ++++++++++++++++++++---- src/supplicant/nm-supplicant-interface.h | 9 ++- 3 files changed, 90 insertions(+), 17 deletions(-) diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 40faa13470..bf3e6ad27a 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -1509,7 +1509,11 @@ request_wireless_scan (NMDeviceWifi *self, nm_supplicant_interface_request_scan (priv->sup_iface, ssids ? (GBytes *const*) ssids->pdata : NULL, - ssids ? ssids->len : 0u); + ssids ? ssids->len : 0u, + NULL, + NULL, + NULL); + request_started = TRUE; } else _LOGD (LOGD_WIFI, "wifi-scan: scanning requested but not allowed at this time"); diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index bfee298445..099c3bf2ca 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -2337,40 +2337,82 @@ nm_supplicant_interface_assoc (NMSupplicantInterface *self, /*****************************************************************************/ +typedef struct { + NMSupplicantInterface *self; + GCancellable *cancellable; + NMSupplicantInterfaceRequestScanCallback callback; + gpointer user_data; +} ScanRequestData; + static void scan_request_cb (GObject *source, GAsyncResult *result, gpointer user_data) { + gs_unref_object NMSupplicantInterface *self_keep_alive = NULL; NMSupplicantInterface *self; gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; + ScanRequestData *data = user_data; + gboolean cancelled = FALSE; 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); - if (error) { - if (_nm_dbus_error_has_name (error, "fi.w1.wpa_supplicant1.Interface.ScanError")) - _LOGD ("request-scan: could not get scan request result: %s", error->message); - else { - g_dbus_error_strip_remote_error (error); - _LOGW ("request-scan: could not get scan request result: %s", error->message); + if (nm_utils_error_is_cancelled (error)) { + if (!data->callback) { + /* the self instance was not kept alive. We also must not touch it. Return. */ + nm_g_object_unref (data->cancellable); + nm_g_slice_free (data); + return; } - } else - _LOGT ("request-scan: request scanning success"); + cancelled = TRUE; + } + + self = data->self; + if (data->callback) { + /* the self instance was kept alive. Balance the reference count. */ + self_keep_alive = self; + } + + /* we don't propagate the error/success. That is, because either answer is not + * reliable. What is important to us is whether the request completed, and + * the current nm_supplicant_interface_get_scanning() state. */ + if (cancelled) + _LOGD ("request-scan: request cancelled"); + else { + if (error) { + if (_nm_dbus_error_has_name (error, "fi.w1.wpa_supplicant1.Interface.ScanError")) + _LOGD ("request-scan: could not get scan request result: %s", error->message); + else { + g_dbus_error_strip_remote_error (error); + _LOGW ("request-scan: could not get scan request result: %s", error->message); + } + } else + _LOGT ("request-scan: request scanning success"); + } + + if (data->callback) + data->callback (self, data->cancellable, data->user_data); + + nm_g_object_unref (data->cancellable); + nm_g_slice_free (data); } void nm_supplicant_interface_request_scan (NMSupplicantInterface *self, GBytes *const*ssids, - guint ssids_len) + guint ssids_len, + GCancellable *cancellable, + NMSupplicantInterfaceRequestScanCallback callback, + gpointer user_data) { NMSupplicantInterfacePrivate *priv; GVariantBuilder builder; + ScanRequestData *data; guint i; g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + nm_assert ( (!cancellable && !callback) + || (G_IS_CANCELLABLE (cancellable) && callback)); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); _LOGT ("request-scan: request scanning (%u ssids)...", ssids_len); @@ -2390,6 +2432,26 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, g_variant_builder_add (&builder, "{sv}", "SSIDs", g_variant_builder_end (&ssids_builder)); } + data = g_slice_new (ScanRequestData); + *data = (ScanRequestData) { + .self = self, + .callback = callback, + .user_data = user_data, + .cancellable = nm_g_object_ref (cancellable), + }; + + if (callback) { + /* A callback was provided. This keeps @self alive. The caller + * must provide a cancellable as the caller must never leave an asynchronous + * operation pending indefinitely. */ + nm_assert (G_IS_CANCELLABLE (cancellable)); + g_object_ref (self); + } else { + /* We don't keep @self alive, and we don't accept a cancellable either. */ + nm_assert (!cancellable); + cancellable = priv->main_cancellable; + } + _dbus_connection_call (self, NM_WPAS_DBUS_IFACE_INTERFACE, "Scan", @@ -2397,9 +2459,9 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, G_VARIANT_TYPE ("()"), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT_MSEC, - priv->main_cancellable, + cancellable, scan_request_cb, - self); + data); } /*****************************************************************************/ diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index eb414f26f5..9ef198167f 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -128,9 +128,16 @@ nm_supplicant_interface_disconnect_async (NMSupplicantInterface * self, NMSupplicantInterfaceDisconnectCb callback, gpointer user_data); +typedef void (*NMSupplicantInterfaceRequestScanCallback) (NMSupplicantInterface *self, + GCancellable *cancellable, + gpointer user_data); + void nm_supplicant_interface_request_scan (NMSupplicantInterface *self, GBytes *const*ssids, - guint ssids_len); + guint ssids_len, + GCancellable *cancellable, + NMSupplicantInterfaceRequestScanCallback callback, + gpointer user_data); NMSupplicantInterfaceState nm_supplicant_interface_get_state (NMSupplicantInterface * self);