supplicant: Handle race condition on WiFi dongle removal
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Fri, 9 Nov 2012 08:54:20 +0000 (10:54 +0200)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Tue, 13 Nov 2012 12:37:45 +0000 (14:37 +0200)
When a WiFi USB dongle is removed, both rtnl and wpa_supplicant will
be telling that the device is gone. While disconnecting and removing
an interface via gsupplicant, check that D-Bus doesn't give us an
error indicating the interface is already unregistered from D-Bus.

The gdb output looks like:
 (gdb) where
 #0  0x0042247c in network_connect (network=0x2) at plugins/wifi.c:1197
 #1  0x004228f0 in disconnect_callback (result=<value optimized out>,
 interface=0x0, user_data=0x99) at plugins/wifi.c:1254
 #2  0x0042b910 in network_remove_result (error=0x4d5458
 "org.freedesktop.DBus.Error.UnknownMethod", iter=<value optimized
 out>,
     user_data=<value optimized out>) at gsupplicant/supplicant.c:3593
 #3  0x0042ede0 in method_call_reply (call=0x4c6fd0,
 user_data=0x4d3be8) at gsupplicant/dbus.c:386
 #4  0x2abfddb4 in complete_pending_call_and_unlock () from
 /usr/lib/libdbus-1.so.3

Also don't call the same callback twice.

gsupplicant/supplicant.c

index cd1225e..d9dfdbb 100644 (file)
@@ -3586,8 +3586,12 @@ static void network_remove_result(const char *error,
 
        SUPPLICANT_DBG("");
 
 
        SUPPLICANT_DBG("");
 
-       if (error != NULL)
+       if (error != NULL) {
                result = -EIO;
                result = -EIO;
+               if (g_strcmp0("org.freedesktop.DBus.Error.UnknownMethod",
+                                               error) == 0)
+                       result = -ECONNABORTED;
+       }
 
        if (data->callback != NULL)
                data->callback(result, data->interface, data->user_data);
 
        if (data->callback != NULL)
                data->callback(result, data->interface, data->user_data);
@@ -3620,11 +3624,21 @@ static void interface_disconnect_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
+       int result = 0;
 
        SUPPLICANT_DBG("");
 
 
        SUPPLICANT_DBG("");
 
-       if (error != NULL && data->callback != NULL)
-               data->callback(-EIO, data->interface, data->user_data);
+       if (error != NULL) {
+               result = -EIO;
+               if (g_strcmp0("org.freedesktop.DBus.Error.UnknownMethod",
+                                               error) == 0)
+                       result = -ECONNABORTED;
+       }
+
+       if (result < 0 && data->callback != NULL) {
+               data->callback(result, data->interface, data->user_data);
+               data->callback = NULL;
+       }
 
        /* If we are disconnecting from previous WPS successful
         * association. i.e.: it did not went through AddNetwork,
 
        /* If we are disconnecting from previous WPS successful
         * association. i.e.: it did not went through AddNetwork,
@@ -3634,7 +3648,10 @@ static void interface_disconnect_result(const char *error,
                return;
        }
 
                return;
        }
 
-       network_remove(data);
+       if (result != -ECONNABORTED)
+               network_remove(data);
+       else
+               dbus_free(data);
 }
 
 int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
 }
 
 int g_supplicant_interface_disconnect(GSupplicantInterface *interface,