From 79256975d15e85b545e2fd74ad3f55183698af89 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 25 Sep 2008 10:02:28 +0000 Subject: [PATCH] 2008-09-25 Dan Williams Fix bgo #549401 (inspired by patch from Alexander Sack) * src/nm-device-ethernet.c - (finish_supplicant_task): clean up scheduled tasks and free memory - (remove_supplicant_interface_error_handler): remove the supplicant error idle callback too - (supplicant_interface_release): rename from supplicant_interface_clean to match nm-device-wifi.c; clean up supplicant interface-related state tasks when the supplicant interface is disposed of - (schedule_state_handler): add scheduled tasks to a list so they can be cleaned up later - (supplicant_mgr_state_cb_handler, supplicant_iface_state_cb_handler, supplicant_iface_connection_state_cb_handler): use finish_supplicant_task() to clean up each completed task - (supplicant_iface_connection_error_cb_handler, supplicant_connection_timeout_cb): clear source id when the task is complete - (supplicant_iface_connection_error_cb): save scheduled task id for later cleanup - (nm_device_ethernet_dispose): clean up any pending supplicant state tasks * src/nm-device-wifi.c - (finish_supplicant_task): clean up scheduled tasks and free memory - (remove_supplicant_interface_error_handler): remove the supplicant error idle callback too - (supplicant_interface_release): clean up supplicant interface-related state tasks when the supplicant interface is disposed of - (schedule_state_handler): add scheduled tasks to a list so they can be cleaned up later - (supplicant_mgr_state_cb_handler, supplicant_iface_state_cb_handler, supplicant_iface_connection_state_cb_handler): use finish_supplicant_task() to clean up each completed task - (supplicant_iface_connection_error_cb_handler): clear source id when the task is complete - (supplicant_iface_connection_error_cb): save scheduled task id for later cleanup - (nm_device_wifi_dispose): clean up any pending supplicant state tasks git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@4105 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- ChangeLog | 41 +++++++ src/nm-device-ethernet.c | 188 +++++++++++++++++++++---------- src/nm-device-wifi.c | 238 +++++++++++++++++++++++---------------- 3 files changed, 314 insertions(+), 153 deletions(-) diff --git a/ChangeLog b/ChangeLog index f5a94afb8d..dfc8e10fb9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2008-09-25 Dan Williams + + Fix bgo #549401 (inspired by patch from Alexander Sack) + + * src/nm-device-ethernet.c + - (finish_supplicant_task): clean up scheduled tasks and free memory + - (remove_supplicant_interface_error_handler): remove the supplicant + error idle callback too + - (supplicant_interface_release): rename from supplicant_interface_clean + to match nm-device-wifi.c; clean up supplicant interface-related + state tasks when the supplicant interface is disposed of + - (schedule_state_handler): add scheduled tasks to a list so they can + be cleaned up later + - (supplicant_mgr_state_cb_handler, supplicant_iface_state_cb_handler, + supplicant_iface_connection_state_cb_handler): use + finish_supplicant_task() to clean up each completed task + - (supplicant_iface_connection_error_cb_handler, + supplicant_connection_timeout_cb): clear source id when the task is + complete + - (supplicant_iface_connection_error_cb): save scheduled task id for + later cleanup + - (nm_device_ethernet_dispose): clean up any pending supplicant state + tasks + + * src/nm-device-wifi.c + - (finish_supplicant_task): clean up scheduled tasks and free memory + - (remove_supplicant_interface_error_handler): remove the supplicant + error idle callback too + - (supplicant_interface_release): clean up supplicant interface-related + state tasks when the supplicant interface is disposed of + - (schedule_state_handler): add scheduled tasks to a list so they can + be cleaned up later + - (supplicant_mgr_state_cb_handler, supplicant_iface_state_cb_handler, + supplicant_iface_connection_state_cb_handler): use + finish_supplicant_task() to clean up each completed task + - (supplicant_iface_connection_error_cb_handler): clear source id when + the task is complete + - (supplicant_iface_connection_error_cb): save scheduled task id for + later cleanup + - (nm_device_wifi_dispose): clean up any pending supplicant state tasks + 2008-09-24 Tambet Ingo * system-settings/plugins/keyfile/plugin.c: Implement unmanaged_devices diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 494fb38f77..a276807ee6 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -18,7 +18,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2005 Red Hat, Inc. + * (C) Copyright 2005 - 2008 Red Hat, Inc. */ #include @@ -73,17 +73,30 @@ typedef enum #define NM_ETHERNET_ERROR (nm_ethernet_error_quark ()) #define NM_TYPE_ETHERNET_ERROR (nm_ethernet_error_get_type ()) +typedef struct SupplicantStateTask { + NMDeviceEthernet *self; + guint32 new_state; + guint32 old_state; + gboolean mgr_task; + guint source_id; +} SupplicantStateTask; + typedef struct Supplicant { NMSupplicantManager *mgr; NMSupplicantInterface *iface; /* signal handler ids */ - guint mgr_state_id; - guint iface_error_id; - guint iface_state_id; - guint iface_con_state_id; + guint mgr_state_id; + guint iface_error_id; + guint iface_state_id; + guint iface_con_state_id; - guint con_timeout_id; + /* Timeouts and idles */ + guint iface_con_error_cb_id; + guint con_timeout_id; + + GSList *iface_tasks; + GSList *mgr_tasks; } Supplicant; typedef struct { @@ -664,7 +677,31 @@ remove_supplicant_timeouts (NMDeviceEthernet *self) } static void -remove_supplicant_interface_connection_error_handler (NMDeviceEthernet *self) +finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source) +{ + NMDeviceEthernet *self = task->self; + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + /* idle/timeout handlers should pass FALSE for remove_source, since they + * will tell glib to remove their source from the mainloop by returning + * FALSE when they exit. When called from this NMDevice's dispose handler, + * remove_source should be TRUE to cancel all outstanding idle/timeout + * handlers asynchronously. + */ + if (task->source_id && remove_source) + g_source_remove (task->source_id); + + if (task->mgr_task) + priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task); + + memset (task, 0, sizeof (SupplicantStateTask)); + g_slice_free (SupplicantStateTask, task); +} + +static void +remove_supplicant_interface_error_handler (NMDeviceEthernet *self) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); @@ -672,15 +709,24 @@ remove_supplicant_interface_connection_error_handler (NMDeviceEthernet *self) g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); priv->supplicant.iface_error_id = 0; } + + if (priv->supplicant.iface_con_error_cb_id > 0) { + g_source_remove (priv->supplicant.iface_con_error_cb_id); + priv->supplicant.iface_con_error_cb_id = 0; + } } static void -supplicant_interface_clean (NMDeviceEthernet *self) +supplicant_interface_release (NMDeviceEthernet *self) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); remove_supplicant_timeouts (self); - remove_supplicant_interface_connection_error_handler (self); + remove_supplicant_interface_error_handler (self); + + /* Clean up all pending supplicant interface state idle tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); if (priv->supplicant.iface_con_state_id) { g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id); @@ -739,7 +785,7 @@ link_timeout_cb (gpointer user_data) nm_info ("Activation (%s/wired): disconnected during authentication," " asking for new key.", nm_device_get_iface (dev)); - supplicant_interface_clean (self); + supplicant_interface_release (self); nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); nm_act_request_request_connection_secrets (req, setting_name, TRUE, @@ -754,42 +800,47 @@ time_out: return FALSE; } -struct state_cb_data { - NMDeviceEthernet *self; - guint32 new_state; - guint32 old_state; -}; - static gboolean schedule_state_handler (NMDeviceEthernet *self, GSourceFunc handler, guint32 new_state, - guint32 old_state) + guint32 old_state, + gboolean mgr_task) { - struct state_cb_data * cb_data; + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + SupplicantStateTask *task; if (new_state == old_state) return TRUE; - cb_data = g_slice_new0 (struct state_cb_data); - cb_data->self = self; - cb_data->new_state = new_state; - cb_data->old_state = old_state; + task = g_slice_new0 (SupplicantStateTask); + if (!task) { + nm_warning ("Not enough memory to process supplicant manager state change."); + return FALSE; + } - g_idle_add (handler, cb_data); + task->self = self; + task->new_state = new_state; + task->old_state = old_state; + task->mgr_task = mgr_task; + task->source_id = g_idle_add (handler, task); + if (mgr_task) + priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task); return TRUE; } static gboolean supplicant_mgr_state_cb_handler (gpointer user_data) { - struct state_cb_data *info = (struct state_cb_data *) user_data; - NMDevice *device = NM_DEVICE (info->self); + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDevice *device = NM_DEVICE (task->self); /* If the supplicant went away, release the supplicant interface */ - if (info->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { - supplicant_interface_clean (info->self); + if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { + supplicant_interface_release (task->self); if (nm_device_get_state (device) > NM_DEVICE_STATE_UNAVAILABLE) { nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, @@ -797,8 +848,7 @@ supplicant_mgr_state_cb_handler (gpointer user_data) } } - g_slice_free (struct state_cb_data, info); - + finish_supplicant_task (task, FALSE); return FALSE; } @@ -814,8 +864,10 @@ supplicant_mgr_state_cb (NMSupplicantInterface * iface, old_state); schedule_state_handler (NM_DEVICE_ETHERNET (user_data), - supplicant_mgr_state_cb_handler, - new_state, old_state); + supplicant_mgr_state_cb_handler, + new_state, + old_state, + TRUE); } static NMSupplicantConfig * @@ -848,17 +900,17 @@ build_supplicant_config (NMDeviceEthernet *self) static gboolean supplicant_iface_state_cb_handler (gpointer user_data) { - struct state_cb_data *info = (struct state_cb_data *) user_data; - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (info->self); - NMDevice *device = NM_DEVICE (info->self); + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self); + NMDevice *device = NM_DEVICE (task->self); - if (info->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { + if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { NMSupplicantConfig *config; const char *iface; gboolean success = FALSE; iface = nm_device_get_iface (device); - config = build_supplicant_config (info->self); + config = build_supplicant_config (task->self); if (config) { success = nm_supplicant_interface_set_config (priv->supplicant.iface, config); g_object_unref (config); @@ -871,17 +923,16 @@ supplicant_iface_state_cb_handler (gpointer user_data) if (!success) nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); - } else if (info->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { NMDeviceState state = nm_device_get_state (device); - supplicant_interface_clean (info->self); + supplicant_interface_release (task->self); if (nm_device_is_activating (device) || state == NM_DEVICE_STATE_ACTIVATED) nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); } - g_slice_free (struct state_cb_data, info); - + finish_supplicant_task (task, FALSE); return FALSE; } @@ -900,18 +951,19 @@ supplicant_iface_state_cb (NMSupplicantInterface * iface, schedule_state_handler (NM_DEVICE_ETHERNET (user_data), supplicant_iface_state_cb_handler, new_state, - old_state); + old_state, + FALSE); } static gboolean supplicant_iface_connection_state_cb_handler (gpointer user_data) { - struct state_cb_data *info = (struct state_cb_data *) user_data; - NMDevice *dev = NM_DEVICE (info->self); + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDevice *dev = NM_DEVICE (task->self); - if (info->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { - remove_supplicant_interface_connection_error_handler (info->self); - remove_supplicant_timeouts (info->self); + if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { + remove_supplicant_interface_error_handler (task->self); + remove_supplicant_timeouts (task->self); /* If this is the initial association during device activation, * schedule the next activation stage. @@ -921,9 +973,9 @@ supplicant_iface_connection_state_cb_handler (gpointer user_data) nm_device_get_iface (dev)); nm_device_activate_schedule_stage3_ip_config_start (dev); } - } else if (info->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) { - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (info->self); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self); /* Start the link timeout so we allow some time for reauthentication */ if (!priv->link_timeout_id) @@ -931,8 +983,7 @@ supplicant_iface_connection_state_cb_handler (gpointer user_data) } } - g_slice_free (struct state_cb_data, info); - + finish_supplicant_task (task, FALSE); return FALSE; } @@ -948,17 +999,20 @@ supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, schedule_state_handler (NM_DEVICE_ETHERNET (user_data), supplicant_iface_connection_state_cb_handler, new_state, - old_state); + old_state, + FALSE); } static gboolean supplicant_iface_connection_error_cb_handler (gpointer user_data) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - supplicant_interface_clean (self); + supplicant_interface_release (self); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + priv->supplicant.iface_con_error_cb_id = 0; return FALSE; } @@ -968,12 +1022,18 @@ supplicant_iface_connection_error_cb (NMSupplicantInterface *iface, const char *message, gpointer user_data) { - NMDevice *device = NM_DEVICE (user_data); + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + guint id; nm_info ("Activation (%s/wired): association request to the supplicant failed: %s - %s", - nm_device_get_iface (device), name, message); + nm_device_get_iface (NM_DEVICE (self)), name, message); - g_idle_add (supplicant_iface_connection_error_cb_handler, device); + if (priv->supplicant.iface_con_error_cb_id) + g_source_remove (priv->supplicant.iface_con_error_cb_id); + + id = g_idle_add (supplicant_iface_connection_error_cb_handler, self); + priv->supplicant.iface_con_error_cb_id = id; } static NMActStageReturn @@ -1017,16 +1077,19 @@ static gboolean supplicant_connection_timeout_cb (gpointer user_data) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); NMActRequest *req; const char *iface; + priv->supplicant.con_timeout_id = 0; + iface = nm_device_get_iface (device); /* Authentication failed, encryption key is probably bad */ nm_info ("Activation (%s/wired): association took too long.", iface); - supplicant_interface_clean (self); + supplicant_interface_release (self); req = nm_device_get_act_request (device); g_assert (req); @@ -1050,7 +1113,7 @@ supplicant_interface_init (NMDeviceEthernet *self) priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr, iface, FALSE); if (!priv->supplicant.iface) { nm_warning ("Couldn't initialize supplicant interface for %s.", iface); - supplicant_interface_clean (self); + supplicant_interface_release (self); return FALSE; } @@ -1290,7 +1353,7 @@ real_deactivate_quickly (NMDevice *device) priv->ppp_manager = NULL; } - supplicant_interface_clean (NM_DEVICE_ETHERNET (device)); + supplicant_interface_release (NM_DEVICE_ETHERNET (device)); } static gboolean @@ -1345,7 +1408,8 @@ real_check_connection_compatible (NMDevice *device, static void nm_device_ethernet_dispose (GObject *object) { - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object); + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); NMNetlinkMonitor *monitor; if (priv->dispose_has_run) { @@ -1355,6 +1419,12 @@ nm_device_ethernet_dispose (GObject *object) priv->dispose_has_run = TRUE; + /* Clean up all pending supplicant tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + while (priv->supplicant.mgr_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE); + monitor = nm_netlink_monitor_get (); if (priv->link_connected_id) { g_signal_handler_disconnect (monitor, priv->link_connected_id); diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 4011475772..9852d4752f 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -17,7 +17,8 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2005 Red Hat, Inc. + * (C) Copyright 2005 - 2008 Red Hat, Inc. + * (C) Copyright 2007 - 2008 Novell, Inc. */ #include @@ -106,20 +107,32 @@ typedef enum #define NM_WIFI_ERROR (nm_wifi_error_quark ()) #define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) +typedef struct SupplicantStateTask { + NMDeviceWifi *self; + guint32 new_state; + guint32 old_state; + gboolean mgr_task; + guint source_id; +} SupplicantStateTask; typedef struct Supplicant { NMSupplicantManager * mgr; NMSupplicantInterface * iface; /* signal handler ids */ - guint mgr_state_id; - guint iface_error_id; - guint iface_state_id; - guint iface_scanned_ap_id; - guint iface_scan_result_id; - guint iface_con_state_id; + guint mgr_state_id; + guint iface_error_id; + guint iface_state_id; + guint iface_scanned_ap_id; + guint iface_scan_result_id; + guint iface_con_state_id; - guint con_timeout_id; + /* Timeouts and idles */ + guint iface_con_error_cb_id; + guint con_timeout_id; + + GSList *mgr_tasks; + GSList *iface_tasks; } Supplicant; struct _NMDeviceWifiPrivate @@ -147,11 +160,11 @@ struct _NMDeviceWifiPrivate guint8 scan_interval; /* seconds */ guint pending_scan_id; - Supplicant supplicant; + Supplicant supplicant; - guint32 failed_link_count; - guint periodic_source_id; - guint link_timeout_id; + guint32 failed_link_count; + guint periodic_source_id; + guint link_timeout_id; /* Static options from driver */ guint8 we_version; @@ -405,7 +418,6 @@ nm_device_wifi_init (NMDeviceWifi * self) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); priv->dispose_has_run = FALSE; - priv->supplicant.iface_error_id = 0; priv->scanning = FALSE; priv->ap_list = NULL; priv->we_version = 0; @@ -589,6 +601,49 @@ supplicant_interface_acquire (NMDeviceWifi *self) return TRUE; } +static void +finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source) +{ + NMDeviceWifi *self = task->self; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* idle/timeout handlers should pass FALSE for remove_source, since they + * will tell glib to remove their source from the mainloop by returning + * FALSE when they exit. When called from this NMDevice's dispose handler, + * remove_source should be TRUE to cancel all outstanding idle/timeout + * handlers asynchronously. + */ + if (task->source_id && remove_source) + g_source_remove (task->source_id); + + if (task->mgr_task) + priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task); + + memset (task, 0, sizeof (SupplicantStateTask)); + g_slice_free (SupplicantStateTask, task); +} + +static void +remove_supplicant_interface_error_handler (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (!priv->supplicant.iface) + return; + + if (priv->supplicant.iface_error_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); + priv->supplicant.iface_error_id = 0; + } + + if (priv->supplicant.iface_con_error_cb_id > 0) { + g_source_remove (priv->supplicant.iface_con_error_cb_id); + priv->supplicant.iface_con_error_cb_id = 0; + } +} + static void supplicant_interface_release (NMDeviceWifi *self) { @@ -603,10 +658,11 @@ supplicant_interface_release (NMDeviceWifi *self) /* Reset the scan interval to be pretty frequent when disconnected */ priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP; - if (priv->supplicant.iface_error_id > 0) { - g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); - priv->supplicant.iface_error_id = 0; - } + remove_supplicant_interface_error_handler (self); + + /* Clean up all pending supplicant interface state idle tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); if (priv->supplicant.iface_state_id > 0) { g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id); @@ -1944,27 +2000,12 @@ supplicant_iface_scanned_ap_cb (NMSupplicantInterface *iface, } -static void -remove_supplicant_interface_connection_error_handler (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (!priv->supplicant.iface) - return; - - if (priv->supplicant.iface_error_id != 0) { - g_signal_handler_disconnect (priv->supplicant.iface, - priv->supplicant.iface_error_id); - priv->supplicant.iface_error_id = 0; - } -} - static void cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - remove_supplicant_interface_connection_error_handler (self); + remove_supplicant_interface_error_handler (self); remove_supplicant_timeouts (self); if (disconnect && priv->supplicant.iface) nm_supplicant_interface_disconnect (priv->supplicant.iface); @@ -2074,20 +2115,15 @@ time_out: return FALSE; } - -struct state_cb_data { - NMDeviceWifi * self; - guint32 new_state; - guint32 old_state; -}; - static gboolean -schedule_state_handler (NMDeviceWifi * self, +schedule_state_handler (NMDeviceWifi *self, GSourceFunc handler, guint32 new_state, - guint32 old_state) + guint32 old_state, + gboolean mgr_task) { - struct state_cb_data * cb_data; + NMDeviceWifiPrivate *priv; + SupplicantStateTask *task; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (handler != NULL, FALSE); @@ -2095,18 +2131,24 @@ schedule_state_handler (NMDeviceWifi * self, if (new_state == old_state) return TRUE; - cb_data = g_slice_new0 (struct state_cb_data); - if (cb_data == NULL) { - nm_warning ("Not enough memory to process supplicant manager state" - " change."); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + task = g_slice_new0 (SupplicantStateTask); + if (!task) { + nm_warning ("Not enough memory to process supplicant manager state change."); return FALSE; } - cb_data->self = self; - cb_data->new_state = new_state; - cb_data->old_state = old_state; + task->self = self; + task->new_state = new_state; + task->old_state = old_state; + task->mgr_task = mgr_task; - g_idle_add (handler, cb_data); + task->source_id = g_idle_add (handler, task); + if (mgr_task) + priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task); return TRUE; } @@ -2114,37 +2156,34 @@ schedule_state_handler (NMDeviceWifi * self, static gboolean supplicant_iface_state_cb_handler (gpointer user_data) { - struct state_cb_data *cb_data = (struct state_cb_data *) user_data; + SupplicantStateTask *task = (SupplicantStateTask *) user_data; NMDeviceWifi *self; NMDeviceWifiPrivate *priv; - guint32 new_state, old_state; - g_return_val_if_fail (cb_data != NULL, FALSE); + g_return_val_if_fail (task != NULL, FALSE); - self = cb_data->self; + self = task->self; priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - new_state = cb_data->new_state; - old_state = cb_data->old_state; nm_info ("(%s): supplicant interface state change: %d -> %d.", nm_device_get_iface (NM_DEVICE (self)), - old_state, - new_state); + task->old_state, + task->new_state); - if (new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { + if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { priv->scan_interval = SCAN_INTERVAL_MIN; /* Request a scan to get latest results */ cancel_pending_scan (self); request_wireless_scan (self); - } else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { cleanup_association_attempt (self, FALSE); supplicant_interface_release (self); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); } - - g_slice_free (struct state_cb_data, cb_data); + + finish_supplicant_task (task, FALSE); return FALSE; } @@ -2160,19 +2199,18 @@ supplicant_iface_state_cb (NMSupplicantInterface * iface, schedule_state_handler (self, supplicant_iface_state_cb_handler, new_state, - old_state); + old_state, + FALSE); } static gboolean supplicant_iface_connection_state_cb_handler (gpointer user_data) { - struct state_cb_data *cb_data = (struct state_cb_data *) user_data; - NMDeviceWifi *self = cb_data->self; + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self = task->self; NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMDevice *dev = NM_DEVICE (self); - guint32 new_state = cb_data->new_state; - guint32 old_state = cb_data->old_state; if (!nm_device_get_act_request (dev)) { /* The device is not activating or already activated; do nothing. */ @@ -2180,12 +2218,12 @@ supplicant_iface_connection_state_cb_handler (gpointer user_data) } nm_info ("(%s): supplicant connection state change: %d -> %d", - nm_device_get_iface (dev), old_state, new_state); + nm_device_get_iface (dev), task->old_state, task->new_state); - priv->scanning = (new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING); + priv->scanning = (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING); - if (new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { - remove_supplicant_interface_connection_error_handler (self); + if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { + remove_supplicant_interface_error_handler (self); remove_supplicant_timeouts (self); /* If this is the initial association during device activation, @@ -2201,7 +2239,7 @@ supplicant_iface_connection_state_cb_handler (gpointer user_data) ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)"); nm_device_activate_schedule_stage3_ip_config_start (dev); } - } else if (new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) { /* Start the link timeout so we allow some time for reauthentication, * use a longer timeout if we are scanning since some cards take a @@ -2215,7 +2253,7 @@ supplicant_iface_connection_state_cb_handler (gpointer user_data) } out: - g_slice_free (struct state_cb_data, cb_data); + finish_supplicant_task (task, FALSE); return FALSE; } @@ -2231,35 +2269,32 @@ supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, schedule_state_handler (self, supplicant_iface_connection_state_cb_handler, new_state, - old_state); + old_state, + FALSE); } static gboolean supplicant_mgr_state_cb_handler (gpointer user_data) { - struct state_cb_data * cb_data = (struct state_cb_data *) user_data; + SupplicantStateTask *task = (SupplicantStateTask *) user_data; NMDeviceWifi *self; NMDeviceWifiPrivate *priv; NMDevice *dev; - guint32 new_state, old_state; NMDeviceState dev_state; - g_return_val_if_fail (cb_data != NULL, FALSE); + g_return_val_if_fail (task != NULL, FALSE); - self = cb_data->self; + self = task->self; priv = NM_DEVICE_WIFI_GET_PRIVATE (self); dev = NM_DEVICE (self); - new_state = cb_data->new_state; - old_state = cb_data->old_state; nm_info ("(%s): supplicant manager is now in state %d (from %d).", nm_device_get_iface (NM_DEVICE (self)), - new_state, - old_state); + task->new_state, task->old_state); /* If the supplicant went away, release the supplicant interface */ - if (new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { + if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { if (priv->supplicant.iface) { cleanup_association_attempt (self, FALSE); supplicant_interface_release (self); @@ -2269,7 +2304,7 @@ supplicant_mgr_state_cb_handler (gpointer user_data) nm_device_state_changed (dev, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); } - } else if (new_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) { + } else if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) { dev_state = nm_device_get_state (dev); if ( priv->enabled && !priv->supplicant.iface @@ -2287,7 +2322,7 @@ supplicant_mgr_state_cb_handler (gpointer user_data) } } - g_slice_free (struct state_cb_data, cb_data); + finish_supplicant_task (task, FALSE); return FALSE; } @@ -2302,25 +2337,27 @@ supplicant_mgr_state_cb (NMSupplicantInterface * iface, schedule_state_handler (self, supplicant_mgr_state_cb_handler, new_state, - old_state); + old_state, + TRUE); } struct iface_con_error_cb_data { - NMDeviceWifi * self; - char * name; - char * message; + NMDeviceWifi *self; + char *name; + char *message; }; - static gboolean supplicant_iface_connection_error_cb_handler (gpointer user_data) { + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; struct iface_con_error_cb_data * cb_data = (struct iface_con_error_cb_data *) user_data; - NMDeviceWifi * self; g_return_val_if_fail (cb_data != NULL, FALSE); self = cb_data->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); if (!nm_device_is_activating (NM_DEVICE (self))) goto out; @@ -2335,6 +2372,7 @@ supplicant_iface_connection_error_cb_handler (gpointer user_data) nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); out: + priv->supplicant.iface_con_error_cb_id = 0; g_free (cb_data->name); g_free (cb_data->message); g_slice_free (struct iface_con_error_cb_data, cb_data); @@ -2348,10 +2386,12 @@ supplicant_iface_connection_error_cb (NMSupplicantInterface * iface, const char * message, NMDeviceWifi * self) { - struct iface_con_error_cb_data * cb_data; - guint id; + NMDeviceWifiPrivate *priv; + struct iface_con_error_cb_data *cb_data; + guint id; g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); cb_data = g_slice_new0 (struct iface_con_error_cb_data); if (cb_data == NULL) { @@ -2363,7 +2403,11 @@ supplicant_iface_connection_error_cb (NMSupplicantInterface * iface, cb_data->name = g_strdup (name); cb_data->message = g_strdup (message); + if (priv->supplicant.iface_con_error_cb_id) + g_source_remove (priv->supplicant.iface_con_error_cb_id); + id = g_idle_add (supplicant_iface_connection_error_cb_handler, cb_data); + priv->supplicant.iface_con_error_cb_id = id; } static void @@ -3095,6 +3139,12 @@ nm_device_wifi_dispose (GObject *object) priv->periodic_source_id = 0; } + /* Clean up all pending supplicant tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + while (priv->supplicant.mgr_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE); + cleanup_association_attempt (self, TRUE); supplicant_interface_release (self);