From 30c52cf0acb0835404b337285935dabd601c7dac Mon Sep 17 00:00:00 2001
From: Alexander Sack <asac@jwsdot.com>
Date: Thu, 17 Sep 2009 00:17:57 +0200
Subject: [PATCH] core: delay ethernet device addition by 5 seconds

Some modems export ethernet data ports and misbehave when network-manager
tries to manage them before they get claimed by modemmanager. Waiting
a few seconds helps to avoid this race.
---
 src/nm-manager.c |   90 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 87 insertions(+), 3 deletions(-)

diff --git a/src/nm-manager.c b/src/nm-manager.c
index fd61116..4b418d7 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -57,6 +57,8 @@
 #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
 #define NM_AUTOIP_DBUS_IFACE   "org.freedesktop.nm_avahi_autoipd"
 
+#define ETHERNET_ADD_TIMEOUT_SEC 5
+
 static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err);
 static void impl_manager_activate_connection (NMManager *manager,
 								  const char *service_name,
@@ -185,8 +187,16 @@ typedef struct {
 	DBusGProxy *aipd_proxy;
 
 	gboolean disposed;
+
+	GList *delayed_device_data;
 } NMManagerPrivate;
 
+struct _DelayedDeviceData {
+	NMManager *manager;
+	NMDevice *device;
+	guint source_id;
+};
+
 #define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
 
 G_DEFINE_TYPE_EXTENDED (NMManager, nm_manager, G_TYPE_OBJECT, 0,
@@ -1512,6 +1522,36 @@ find_device_by_ifindex (NMManager *self, guint32 ifindex)
 }
 
 static void
+delayed_device_data_remove (gpointer data)
+{
+	struct _DelayedDeviceData *dd = data;
+	g_source_remove (dd->source_id);
+}
+
+static void
+delayed_device_data_free (gpointer data)
+{
+	struct _DelayedDeviceData *dd = data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (dd->manager);
+	g_object_unref (dd->device);
+	priv->delayed_device_data = g_list_remove (priv->delayed_device_data, dd);
+	g_free(dd);
+}
+
+static gboolean
+delayed_device_addition (gpointer data)
+{
+	struct _DelayedDeviceData *dd = data;
+
+	add_device (dd->manager, dd->device);
+
+	/* will get unreffed on timeout free */
+	g_object_ref (dd->device);
+
+	return FALSE;
+}
+
+static void
 udev_device_added_cb (NMUdevManager *udev_mgr,
                       GUdevDevice *udev_device,
                       NMDeviceCreatorFn creator_fn,
@@ -1527,8 +1567,45 @@ udev_device_added_cb (NMUdevManager *udev_mgr,
 		return;
 
 	device = creator_fn (udev_mgr, udev_device, priv->sleeping);
-	if (device)
-		add_device (self, NM_DEVICE (device));
+	if (device) {
+		if (NM_IS_DEVICE_ETHERNET (device)) {
+			struct _DelayedDeviceData *dd = g_new0 (struct _DelayedDeviceData, 1);
+			guint delayed_id;
+			delayed_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+			                            ETHERNET_ADD_TIMEOUT_SEC,
+			                            delayed_device_addition,
+			                            dd,
+			                            delayed_device_data_free);
+			dd->manager = self;
+			dd->device = NM_DEVICE(device);
+			dd->source_id = delayed_id;
+			priv->delayed_device_data = g_list_append (priv->delayed_device_data, dd);
+		} else {
+			add_device (self, NM_DEVICE (device));
+		}
+	}
+}
+
+static gpointer
+find_delayed_device_data_by_ifindex (NMManager *self, guint32 ifindex)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GList *iter = priv->delayed_device_data;
+	for (; iter; iter = iter -> next) {
+		NMDevice *device = ((struct _DelayedDeviceData *) iter->data)->device;
+		gint candidate_idx = 0;
+
+		if (NM_IS_DEVICE_ETHERNET (device))
+			candidate_idx = nm_device_ethernet_get_ifindex (NM_DEVICE_ETHERNET (device));
+		else if (NM_IS_DEVICE_WIFI (device))
+			candidate_idx = nm_device_wifi_get_ifindex (NM_DEVICE_WIFI (device));
+		else if (NM_IS_DEVICE_OLPC_MESH (device))
+			candidate_idx = nm_device_olpc_mesh_get_ifindex (NM_DEVICE_OLPC_MESH (device));
+
+		if (candidate_idx == ifindex)
+			return iter->data;
+	}
+	return NULL;
 }
 
 static void
@@ -1543,8 +1620,13 @@ udev_device_removed_cb (NMUdevManager *manager,
 
 	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
 	device = find_device_by_ifindex (self, ifindex);
-	if (device)
+	if (device) {
 		priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE);
+	} else {
+		gpointer delayed_device_data = find_delayed_device_data_by_ifindex (self, ifindex);
+		if (delayed_device_data)
+			delayed_device_data_remove (delayed_device_data);
+	}
 }
 
 static void
@@ -2725,6 +2807,8 @@ dispose (GObject *object)
 	if (priv->bluez_mgr)
 		g_object_unref (priv->bluez_mgr);
 
+	g_list_foreach (priv->delayed_device_data, (GFunc) delayed_device_data_remove, NULL);
+
 	G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
 }
 
-- 
1.6.3.3

