d4c1522
From 07281f21a71fa333288821ad863accfeedfff567 Mon Sep 17 00:00:00 2001
d4c1522
From: Dan Williams <dcbw@redhat.com>
d4c1522
Date: Mon, 9 Dec 2013 12:55:04 -0600
d4c1522
Subject: [PATCH] core: delay startup complete until carrier is found or
d4c1522
 timeout (rh #1034921) (rh #1030583)
d4c1522
d4c1522
---
d4c1522
 src/devices/nm-device.c    | 177 ++++++++++++++++++++++++++++++++++-----------
d4c1522
 src/nm-active-connection.c |   8 ++
d4c1522
 2 files changed, 142 insertions(+), 43 deletions(-)
d4c1522
d4c1522
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
d4c1522
index 578ccb0..96a5d44 100644
d4c1522
--- a/src/devices/nm-device.c
d4c1522
+++ b/src/devices/nm-device.c
d4c1522
@@ -184,15 +184,15 @@ typedef struct {
d4c1522
 	gboolean initialized;
d4c1522
 	gboolean in_state_changed;
d4c1522
 
d4c1522
 	NMDeviceState state;
d4c1522
 	NMDeviceStateReason state_reason;
d4c1522
 	QueuedState   queued_state;
d4c1522
 	guint queued_ip_config_id;
d4c1522
-	guint pending_actions;
d4c1522
+	GArray *pending_actions;
d4c1522
 
d4c1522
 	char *        udi;
d4c1522
 	char *        path;
d4c1522
 	char *        iface;   /* may change, could be renamed by user */
d4c1522
 	int           ifindex;
d4c1522
 	gboolean      is_software;
d4c1522
 	char *        ip_iface;
d4c1522
@@ -224,14 +224,15 @@ typedef struct {
d4c1522
 	gpointer        act_source6_func;
d4c1522
 
d4c1522
 	/* Link stuff */
d4c1522
 	guint           link_connected_id;
d4c1522
 	guint           link_disconnected_id;
d4c1522
 	guint           carrier_defer_id;
d4c1522
 	gboolean        carrier;
d4c1522
+	guint           carrier_wait_id;
d4c1522
 	gboolean        ignore_carrier;
d4c1522
 
d4c1522
 	/* Generic DHCP stuff */
d4c1522
 	NMDHCPManager * dhcp_manager;
d4c1522
 	guint32         dhcp_timeout;
d4c1522
 	GByteArray *    dhcp_anycast_address;
d4c1522
 
d4c1522
@@ -390,14 +391,15 @@ nm_device_init (NMDevice *self)
d4c1522
 	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
d4c1522
 	priv->state = NM_DEVICE_STATE_UNMANAGED;
d4c1522
 	priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
d4c1522
 	priv->dhcp_timeout = 0;
d4c1522
 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
d4c1522
 	priv->autoconnect = DEFAULT_AUTOCONNECT;
d4c1522
 	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
d4c1522
+	priv->pending_actions = g_array_sized_new (FALSE, TRUE, sizeof (GQuark), 5);
d4c1522
 }
d4c1522
 
d4c1522
 static void
d4c1522
 update_accept_ra_save (NMDevice *self)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv;
d4c1522
 	const char *ip_iface;
d4c1522
@@ -1154,14 +1156,20 @@ nm_device_set_carrier (NMDevice *device, gboolean carrier)
d4c1522
 	if (priv->carrier) {
d4c1522
 		nm_log_info (LOGD_DEVICE, "(%s): link connected", iface);
d4c1522
 		if (priv->carrier_defer_id) {
d4c1522
 			g_source_remove (priv->carrier_defer_id);
d4c1522
 			priv->carrier_defer_id = 0;
d4c1522
 		}
d4c1522
 		klass->carrier_changed (device, TRUE);
d4c1522
+
d4c1522
+		if (priv->carrier_wait_id) {
d4c1522
+			g_source_remove (priv->carrier_wait_id);
d4c1522
+			priv->carrier_wait_id = 0;
d4c1522
+			nm_device_remove_pending_action (device, "carrier wait");
d4c1522
+		}
d4c1522
 	} else if (state <= NM_DEVICE_STATE_DISCONNECTED) {
d4c1522
 		nm_log_info (LOGD_DEVICE, "(%s): link disconnected", iface);
d4c1522
 		klass->carrier_changed (device, FALSE);
d4c1522
 	} else {
d4c1522
 		nm_log_info (LOGD_DEVICE, "(%s): link disconnected (deferring action for %d seconds)",
d4c1522
 		             iface, LINK_DISCONNECT_DELAY);
d4c1522
 		priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY,
d4c1522
@@ -5110,17 +5118,28 @@ nm_device_start_ip_check (NMDevice *self)
d4c1522
 	/* If no ping was started, just advance to SECONDARIES */
d4c1522
 	if (!priv->gw_ping.pid)
d4c1522
 		nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
d4c1522
 }
d4c1522
 
d4c1522
 /****************************************************************/
d4c1522
 
d4c1522
+static gboolean
d4c1522
+carrier_wait_timeout (gpointer user_data)
d4c1522
+{
d4c1522
+	NMDevice *self = NM_DEVICE (user_data);
d4c1522
+
d4c1522
+	NM_DEVICE_GET_PRIVATE (self)->carrier_wait_id = 0;
d4c1522
+	nm_device_remove_pending_action (self, "carrier wait");
d4c1522
+	return G_SOURCE_REMOVE;
d4c1522
+}
d4c1522
+
d4c1522
 gboolean
d4c1522
 nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
d4c1522
 {
d4c1522
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
d4c1522
 	gboolean success;
d4c1522
 	guint32 tries = 0;
d4c1522
 
d4c1522
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
d4c1522
 
d4c1522
 	if (nm_device_is_up (self))
d4c1522
 		goto out;
d4c1522
@@ -5138,14 +5157,28 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
d4c1522
 		g_usleep (200);
d4c1522
 
d4c1522
 	if (!nm_device_is_up (self)) {
d4c1522
 		nm_log_warn (LOGD_HW, "(%s): device not up after timeout!", nm_device_get_iface (self));
d4c1522
 		return FALSE;
d4c1522
 	}
d4c1522
 
d4c1522
+	/* Devices that support carrier detect must be IFF_UP to report carrier
d4c1522
+	 * changes; so after setting the device IFF_UP we must suppress startup
d4c1522
+	 * complete (via a pending action) until either the carrier turns on, or
d4c1522
+	 * a timeout is reached.
d4c1522
+	 */
d4c1522
+	if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
d4c1522
+		if (priv->carrier_wait_id) {
d4c1522
+			g_source_remove (priv->carrier_wait_id);
d4c1522
+			nm_device_remove_pending_action (self, "carrier wait");
d4c1522
+		}
d4c1522
+		priv->carrier_wait_id = g_timeout_add_seconds (5, carrier_wait_timeout, self);
d4c1522
+		nm_device_add_pending_action (self, "carrier wait");
d4c1522
+	}
d4c1522
+
d4c1522
 out:
d4c1522
 	/* Can only get HW address of some devices when they are up */
d4c1522
 	nm_device_update_hw_address (self);
d4c1522
 
d4c1522
 	_update_ip4_address (self);
d4c1522
 	return TRUE;
d4c1522
 }
d4c1522
@@ -5301,14 +5334,19 @@ dispose (GObject *object)
d4c1522
 	}
d4c1522
 
d4c1522
 	if (priv->cp_updated_id) {
d4c1522
 	    g_signal_handler_disconnect (priv->con_provider, priv->cp_updated_id);
d4c1522
 	    priv->cp_updated_id = 0;
d4c1522
 	}
d4c1522
 
d4c1522
+	if (priv->carrier_wait_id) {
d4c1522
+		g_source_remove (priv->carrier_wait_id);
d4c1522
+		priv->carrier_wait_id = 0;
d4c1522
+	}
d4c1522
+
d4c1522
 	g_hash_table_unref (priv->available_connections);
d4c1522
 	priv->available_connections = NULL;
d4c1522
 
d4c1522
 	activation_source_clear (self, TRUE, AF_INET);
d4c1522
 	activation_source_clear (self, TRUE, AF_INET6);
d4c1522
 
d4c1522
 	clear_act_request (self);
d4c1522
@@ -5331,14 +5369,17 @@ finalize (GObject *object)
d4c1522
 
d4c1522
 	if (priv->dhcp_manager)
d4c1522
 		g_object_unref (priv->dhcp_manager);
d4c1522
 
d4c1522
 	if (priv->fw_manager)
d4c1522
 		g_object_unref (priv->fw_manager);
d4c1522
 
d4c1522
+	g_array_free (priv->pending_actions, TRUE);
d4c1522
+	priv->pending_actions = NULL;
d4c1522
+
d4c1522
 	g_free (priv->udi);
d4c1522
 	g_free (priv->path);
d4c1522
 	g_free (priv->iface);
d4c1522
 	g_free (priv->ip_iface);
d4c1522
 	g_free (priv->driver);
d4c1522
 	g_free (priv->driver_version);
d4c1522
 	g_free (priv->firmware_version);
d4c1522
@@ -5931,46 +5972,54 @@ nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
d4c1522
 
d4c1522
 gboolean
d4c1522
 nm_device_get_firmware_missing (NMDevice *self)
d4c1522
 {
d4c1522
 	return NM_DEVICE_GET_PRIVATE (self)->firmware_missing;
d4c1522
 }
d4c1522
 
d4c1522
+#define QUEUED_PREFIX "queued state change to "
d4c1522
+
d4c1522
 static const char *
d4c1522
-state_to_string (NMDeviceState state)
d4c1522
+queued_state_to_string (NMDeviceState state)
d4c1522
 {
d4c1522
 	switch (state) {
d4c1522
 	case NM_DEVICE_STATE_UNMANAGED:
d4c1522
-		return "unmanaged";
d4c1522
+		return QUEUED_PREFIX "unmanaged";
d4c1522
 	case NM_DEVICE_STATE_UNAVAILABLE:
d4c1522
-		return "unavailable";
d4c1522
+		return QUEUED_PREFIX "unavailable";
d4c1522
 	case NM_DEVICE_STATE_DISCONNECTED:
d4c1522
-		return "disconnected";
d4c1522
+		return QUEUED_PREFIX "disconnected";
d4c1522
 	case NM_DEVICE_STATE_PREPARE:
d4c1522
-		return "prepare";
d4c1522
+		return QUEUED_PREFIX "prepare";
d4c1522
 	case NM_DEVICE_STATE_CONFIG:
d4c1522
-		return "config";
d4c1522
+		return QUEUED_PREFIX "config";
d4c1522
 	case NM_DEVICE_STATE_NEED_AUTH:
d4c1522
-		return "need-auth";
d4c1522
+		return QUEUED_PREFIX "need-auth";
d4c1522
 	case NM_DEVICE_STATE_IP_CONFIG:
d4c1522
-		return "ip-config";
d4c1522
+		return QUEUED_PREFIX "ip-config";
d4c1522
 	case NM_DEVICE_STATE_IP_CHECK:
d4c1522
-		return "ip-check";
d4c1522
+		return QUEUED_PREFIX "ip-check";
d4c1522
 	case NM_DEVICE_STATE_SECONDARIES:
d4c1522
-		return "secondaries";
d4c1522
+		return QUEUED_PREFIX "secondaries";
d4c1522
 	case NM_DEVICE_STATE_ACTIVATED:
d4c1522
-		return "activated";
d4c1522
+		return QUEUED_PREFIX "activated";
d4c1522
 	case NM_DEVICE_STATE_DEACTIVATING:
d4c1522
-		return "deactivating";
d4c1522
+		return QUEUED_PREFIX "deactivating";
d4c1522
 	case NM_DEVICE_STATE_FAILED:
d4c1522
-		return "failed";
d4c1522
+		return QUEUED_PREFIX "failed";
d4c1522
 	default:
d4c1522
 		break;
d4c1522
 	}
d4c1522
-	return "unknown";
d4c1522
+	return QUEUED_PREFIX "unknown";
d4c1522
+}
d4c1522
+
d4c1522
+static const char *
d4c1522
+state_to_string (NMDeviceState state)
d4c1522
+{
d4c1522
+	return queued_state_to_string (state) + strlen (QUEUED_PREFIX);
d4c1522
 }
d4c1522
 
d4c1522
 static const char *
d4c1522
 reason_to_string (NMDeviceStateReason reason)
d4c1522
 {
d4c1522
 	switch (reason) {
d4c1522
 	case NM_DEVICE_STATE_REASON_NONE:
d4c1522
@@ -6083,21 +6132,14 @@ reason_to_string (NMDeviceStateReason reason)
d4c1522
 		return "secondary-connection-failed";
d4c1522
 	default:
d4c1522
 		break;
d4c1522
 	}
d4c1522
 	return "unknown";
d4c1522
 }
d4c1522
 
d4c1522
-static inline gboolean
d4c1522
-state_implies_pending_action (NMDeviceState state)
d4c1522
-{
d4c1522
-	return (   state >= NM_DEVICE_STATE_PREPARE
d4c1522
-	        && state < NM_DEVICE_STATE_ACTIVATED);
d4c1522
-}
d4c1522
-
d4c1522
 void
d4c1522
 nm_device_state_changed (NMDevice *device,
d4c1522
                          NMDeviceState state,
d4c1522
                          NMDeviceStateReason reason)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
 	NMDeviceState old_state;
d4c1522
@@ -6131,18 +6173,14 @@ nm_device_state_changed (NMDevice *device,
d4c1522
 		return;
d4c1522
 	}
d4c1522
 
d4c1522
 	old_state = priv->state;
d4c1522
 	priv->state = state;
d4c1522
 	priv->state_reason = reason;
d4c1522
 
d4c1522
-	if (    state_implies_pending_action (state)
d4c1522
-	    && !state_implies_pending_action (old_state))
d4c1522
-		nm_device_add_pending_action (device, "activation");
d4c1522
-
d4c1522
 	nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]",
d4c1522
 	             nm_device_get_iface (device),
d4c1522
 	             state_to_string (old_state),
d4c1522
 	             state_to_string (state),
d4c1522
 	             reason_to_string (reason),
d4c1522
 	             old_state,
d4c1522
 	             state,
d4c1522
@@ -6300,18 +6338,14 @@ nm_device_state_changed (NMDevice *device,
d4c1522
 	if (old_state == NM_DEVICE_STATE_ACTIVATED)
d4c1522
 		nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL);
d4c1522
 
d4c1522
 	/* Dispose of the cached activation request */
d4c1522
 	if (req)
d4c1522
 		g_object_unref (req);
d4c1522
 
d4c1522
-	if (    state_implies_pending_action (old_state)
d4c1522
-	    && !state_implies_pending_action (state))
d4c1522
-		nm_device_remove_pending_action (device, "activation");
d4c1522
-
d4c1522
 	priv->in_state_changed = FALSE;
d4c1522
 }
d4c1522
 
d4c1522
 static gboolean
d4c1522
 queued_set_state (gpointer user_data)
d4c1522
 {
d4c1522
 	NMDevice *self = NM_DEVICE (user_data);
d4c1522
@@ -6330,15 +6364,15 @@ queued_set_state (gpointer user_data)
d4c1522
 		 */
d4c1522
 		priv->queued_state.id = 0;
d4c1522
 		new_state = priv->queued_state.state;
d4c1522
 		new_reason = priv->queued_state.reason;
d4c1522
 		nm_device_queued_state_clear (self);
d4c1522
 
d4c1522
 		nm_device_state_changed (self, new_state, new_reason);
d4c1522
-		nm_device_remove_pending_action (self, "queued state change");
d4c1522
+		nm_device_remove_pending_action (self, queued_state_to_string (new_state));
d4c1522
 	} else {
d4c1522
 		g_warn_if_fail (priv->queued_state.state == NM_DEVICE_STATE_UNKNOWN);
d4c1522
 		g_warn_if_fail (priv->queued_state.reason == NM_DEVICE_STATE_REASON_NONE);
d4c1522
 	}
d4c1522
 	return FALSE;
d4c1522
 }
d4c1522
 
d4c1522
@@ -6349,29 +6383,37 @@ nm_device_queue_state (NMDevice *self,
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv;
d4c1522
 
d4c1522
 	g_return_if_fail (NM_IS_DEVICE (self));
d4c1522
 
d4c1522
 	priv = NM_DEVICE_GET_PRIVATE (self);
d4c1522
 
d4c1522
+	/* "lock" the pending actions so that if there was a previously
d4c1522
+	 * queued action that's about to be cleared, that doesn't drop
d4c1522
+	 * the pending actions to 0 before we add the new pending action.
d4c1522
+	 */
d4c1522
+	nm_device_add_pending_action (self, "queued state lock");
d4c1522
+
d4c1522
 	/* We should only ever have one delayed state transition at a time */
d4c1522
 	if (priv->queued_state.id) {
d4c1522
 		if (priv->queued_state.state == state)
d4c1522
 			return;
d4c1522
 		nm_log_warn (LOGD_DEVICE, "(%s): overwriting previously queued state change to %s (%s)",
d4c1522
 					 nm_device_get_iface (self),
d4c1522
 					 state_to_string (priv->queued_state.state),
d4c1522
 					 reason_to_string (priv->queued_state.reason));
d4c1522
 		nm_device_queued_state_clear (self);
d4c1522
 	}
d4c1522
 
d4c1522
 	priv->queued_state.state = state;
d4c1522
 	priv->queued_state.reason = reason;
d4c1522
 	priv->queued_state.id = g_idle_add (queued_set_state, self);
d4c1522
-	nm_device_add_pending_action (self, "queued state change");
d4c1522
+	nm_device_add_pending_action (self, queued_state_to_string (state));
d4c1522
+
d4c1522
+	nm_device_remove_pending_action (self, "queued state lock");
d4c1522
 
d4c1522
 	nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s due to %s (id %d)",
d4c1522
 	            nm_device_get_iface (self), state_to_string (state), reason_to_string (reason),
d4c1522
 	            priv->queued_state.id);
d4c1522
 }
d4c1522
 
d4c1522
 NMDeviceState
d4c1522
@@ -6391,15 +6433,15 @@ nm_device_queued_state_clear (NMDevice *self)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
d4c1522
 
d4c1522
 	if (priv->queued_state.id) {
d4c1522
 		nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
d4c1522
 		            nm_device_get_iface (self), priv->queued_state.id);
d4c1522
 		g_source_remove (priv->queued_state.id);
d4c1522
-		nm_device_remove_pending_action (self, "queued state change");
d4c1522
+		nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state));
d4c1522
 	}
d4c1522
 	memset (&priv->queued_state, 0, sizeof (priv->queued_state));
d4c1522
 }
d4c1522
 
d4c1522
 NMDeviceState
d4c1522
 nm_device_get_state (NMDevice *device)
d4c1522
 {
d4c1522
@@ -7102,40 +7144,89 @@ nm_device_set_hw_addr (NMDevice *device, const guint8 *addr,
d4c1522
 	}
d4c1522
 	nm_device_bring_up (device, TRUE, NULL);
d4c1522
 	g_free (mac_str);
d4c1522
 
d4c1522
 	return success;
d4c1522
 }
d4c1522
 
d4c1522
+/**
d4c1522
+ * nm_device_add_pending_action():
d4c1522
+ * @device: the #NMDevice to add the pending action to
d4c1522
+ * @action: a static string that identifies the action
d4c1522
+ *
d4c1522
+ * Adds a pending action to the device.
d4c1522
+ */
d4c1522
 void
d4c1522
 nm_device_add_pending_action (NMDevice *device, const char *action)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
+	GQuark qaction;
d4c1522
+	guint i;
d4c1522
 
d4c1522
-	priv->pending_actions++;
d4c1522
-	nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): %s",
d4c1522
-	            nm_device_get_iface (device), priv->pending_actions, action);
d4c1522
+	qaction = g_quark_from_static_string (action);
d4c1522
 
d4c1522
-	if (priv->pending_actions == 1)
d4c1522
+	/* Shouldn't ever add the same pending action twice */
d4c1522
+	for (i = 0; i < priv->pending_actions->len; i++) {
d4c1522
+		if (qaction == g_array_index (priv->pending_actions, GQuark, i)) {
d4c1522
+			nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already added",
d4c1522
+			             nm_device_get_iface (device),
d4c1522
+			             priv->pending_actions->len,
d4c1522
+			             action);
d4c1522
+			g_warn_if_reached ();
d4c1522
+			return;
d4c1522
+		}
d4c1522
+	}
d4c1522
+
d4c1522
+	g_array_prepend_val (priv->pending_actions, qaction);
d4c1522
+	nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'",
d4c1522
+	            nm_device_get_iface (device),
d4c1522
+	            priv->pending_actions->len,
d4c1522
+	            action);
d4c1522
+
d4c1522
+	if (priv->pending_actions->len == 1)
d4c1522
 		g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
d4c1522
 }
d4c1522
 
d4c1522
+/**
d4c1522
+ * nm_device_remove_pending_action():
d4c1522
+ * @device: the #NMDevice to remove the pending action from
d4c1522
+ * @action: a static string that identifies the action
d4c1522
+ *
d4c1522
+ * Removes a pending action previously added by nm_device_add_pending_action().
d4c1522
+ */
d4c1522
 void
d4c1522
 nm_device_remove_pending_action (NMDevice *device, const char *action)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
+	GQuark qaction;
d4c1522
+	guint i;
d4c1522
 
d4c1522
-	priv->pending_actions--;
d4c1522
-	nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): %s",
d4c1522
-	            nm_device_get_iface (device), priv->pending_actions, action);
d4c1522
+	qaction = g_quark_from_static_string (action);
d4c1522
 
d4c1522
-	if (priv->pending_actions == 0)
d4c1522
-		g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
d4c1522
+	/* Shouldn't ever add the same pending action twice */
d4c1522
+	for (i = 0; i < priv->pending_actions->len; i++) {
d4c1522
+		if (qaction == g_array_index (priv->pending_actions, GQuark, i)) {
d4c1522
+			g_array_remove_index_fast (priv->pending_actions, i);
d4c1522
+			nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'",
d4c1522
+			            nm_device_get_iface (device),
d4c1522
+			            priv->pending_actions->len,
d4c1522
+			            action);
d4c1522
+
d4c1522
+			if (priv->pending_actions->len == 0)
d4c1522
+				g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
d4c1522
+			return;
d4c1522
+		}
d4c1522
+	}
d4c1522
+
d4c1522
+	nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' never added",
d4c1522
+	             nm_device_get_iface (device),
d4c1522
+	             priv->pending_actions->len,
d4c1522
+	             action);
d4c1522
 }
d4c1522
 
d4c1522
 gboolean
d4c1522
 nm_device_has_pending_action (NMDevice *device)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
 
d4c1522
-	return priv->pending_actions > 0;
d4c1522
+	return priv->pending_actions->len > 0;
d4c1522
 }
d4c1522
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
d4c1522
index 6a825bc..34f438c 100644
d4c1522
--- a/src/nm-active-connection.c
d4c1522
+++ b/src/nm-active-connection.c
d4c1522
@@ -135,14 +135,20 @@ nm_active_connection_set_state (NMActiveConnection *self,
d4c1522
 
d4c1522
 	if (   new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
d4c1522
 	    || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
d4c1522
 		nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (priv->connection),
d4c1522
 		                                         (guint64) time (NULL), TRUE);
d4c1522
 	}
d4c1522
 
d4c1522
+	if (priv->device) {
d4c1522
+		if (   old_state < NM_ACTIVE_CONNECTION_STATE_ACTIVATED
d4c1522
+		    && new_state >= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
d4c1522
+			nm_device_remove_pending_action (priv->device, "activation");
d4c1522
+	}
d4c1522
+
d4c1522
 	if (priv->state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
d4c1522
 		/* Device is no longer relevant when deactivated */
d4c1522
 		g_clear_object (&priv->device);
d4c1522
 		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEVICES);
d4c1522
 	}
d4c1522
 }
d4c1522
 
d4c1522
@@ -262,8 +268,10 @@ set_property (GObject *object, guint pro
d4c1522
 	case PROP_INT_DEVICE:
d4c1522
 		g_warn_if_fail (priv->device == NULL);
d4c1522
 		priv->device = g_value_dup_object (value);
d4c1522
-		if (priv->device)
d4c1522
+		if (priv->device) {
d4c1522
 			g_warn_if_fail (priv->device != priv->master);
d4c1522
+			nm_device_add_pending_action (priv->device, "activation");
d4c1522
+		}
d4c1522
 		break;
d4c1522
 	case PROP_INT_USER_REQUESTED:
d4c1522
 		priv->user_requested = g_value_get_boolean (value);
d4c1522
-- 
d4c1522
1.8.3.1
d4c1522
d4c1522
From 813ea5995bb5a35c115c46f30eefe18db2886afb Mon Sep 17 00:00:00 2001
d4c1522
From: Thomas Haller <thaller@redhat.com>
d4c1522
Date: Mon, 16 Dec 2013 16:52:36 +0100
d4c1522
Subject: [PATCH 1/2] core: allow dynamic strings for pending action names
d4c1522
d4c1522
Use a GSList of the string values, instead of an array of GQuarks.
d4c1522
Using GQuarks does not allow to add arbitrary strings, because they
d4c1522
would leak the internalized strings.  The next patch will begin
d4c1522
using unique, non-const action strings.
d4c1522
d4c1522
Given the rather small number of expected pending states, a singly
d4c1522
linked list seems appropriate.
d4c1522
d4c1522
Signed-off-by: Thomas Haller <thaller@redhat.com>
d4c1522
d4c1522
(some fixes and simplifications by dcbw based on patch reviews)
d4c1522
---
d4c1522
 src/devices/nm-device.c | 49 +++++++++++++++++++++++++------------------------
d4c1522
 1 file changed, 25 insertions(+), 24 deletions(-)
d4c1522
d4c1522
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
d4c1522
index c1c7c69..e4644bf 100644
d4c1522
--- a/src/devices/nm-device.c
d4c1522
+++ b/src/devices/nm-device.c
d4c1522
@@ -184,15 +184,15 @@ typedef struct {
d4c1522
 	gboolean initialized;
d4c1522
 	gboolean in_state_changed;
d4c1522
 
d4c1522
 	NMDeviceState state;
d4c1522
 	NMDeviceStateReason state_reason;
d4c1522
 	QueuedState   queued_state;
d4c1522
 	guint queued_ip_config_id;
d4c1522
-	GArray *pending_actions;
d4c1522
+	GSList *pending_actions;
d4c1522
 
d4c1522
 	char *        udi;
d4c1522
 	char *        path;
d4c1522
 	char *        iface;   /* may change, could be renamed by user */
d4c1522
 	int           ifindex;
d4c1522
 	gboolean      is_software;
d4c1522
 	char *        ip_iface;
d4c1522
@@ -391,15 +391,14 @@ nm_device_init (NMDevice *self)
d4c1522
 	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
d4c1522
 	priv->state = NM_DEVICE_STATE_UNMANAGED;
d4c1522
 	priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
d4c1522
 	priv->dhcp_timeout = 0;
d4c1522
 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
d4c1522
 	priv->autoconnect = DEFAULT_AUTOCONNECT;
d4c1522
 	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
d4c1522
-	priv->pending_actions = g_array_sized_new (FALSE, TRUE, sizeof (GQuark), 5);
d4c1522
 }
d4c1522
d4c1522
 static void
d4c1522
 update_accept_ra_save (NMDevice *self)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv;
d4c1522
 	const char *ip_iface;
d4c1522
@@ -5368,16 +5367,15 @@ finalize (GObject *object)
d4c1522
 
d4c1522
 	if (priv->dhcp_manager)
d4c1522
 		g_object_unref (priv->dhcp_manager);
d4c1522
 
d4c1522
 	if (priv->fw_manager)
d4c1522
 		g_object_unref (priv->fw_manager);
d4c1522
 
d4c1522
-	g_array_free (priv->pending_actions, TRUE);
d4c1522
-	priv->pending_actions = NULL;
d4c1522
+	g_slist_free_full (priv->pending_actions, g_free);
d4c1522
 
d4c1522
 	g_free (priv->udi);
d4c1522
 	g_free (priv->path);
d4c1522
 	g_free (priv->iface);
d4c1522
 	g_free (priv->ip_iface);
d4c1522
 	g_free (priv->driver);
d4c1522
 	g_free (priv->driver_version);
d4c1522
@@ -7153,78 +7152,80 @@ nm_device_set_hw_addr (NMDevice *device, const guint8 *addr,
d4c1522
  *
d4c1522
  * Adds a pending action to the device.
d4c1522
  */
d4c1522
 void
d4c1522
 nm_device_add_pending_action (NMDevice *device, const char *action)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
-	GQuark qaction;
d4c1522
-	guint i;
d4c1522
+	GSList *iter;
d4c1522
+	guint count;
d4c1522
 
d4c1522
-	qaction = g_quark_from_static_string (action);
d4c1522
+	g_return_if_fail (action);
d4c1522
 
d4c1522
 	/* Shouldn't ever add the same pending action twice */
d4c1522
-	for (i = 0; i < priv->pending_actions->len; i++) {
d4c1522
-		if (qaction == g_array_index (priv->pending_actions, GQuark, i)) {
d4c1522
+	for (iter = priv->pending_actions; iter; iter = iter->next) {
d4c1522
+		if (!strcmp (action, iter->data)) {
d4c1522
 			nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already added",
d4c1522
 			             nm_device_get_iface (device),
d4c1522
-			             priv->pending_actions->len,
d4c1522
+			             g_slist_length (priv->pending_actions),
d4c1522
 			             action);
d4c1522
-			g_warn_if_reached ();
d4c1522
-			return;
d4c1522
+			g_return_val_if_reached (FALSE);
d4c1522
 		}
d4c1522
 	}
d4c1522
 
d4c1522
-	g_array_prepend_val (priv->pending_actions, qaction);
d4c1522
+	priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action));
d4c1522
+	count = g_slist_length (priv->pending_actions);
d4c1522
+
d4c1522
 	nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'",
d4c1522
 	            nm_device_get_iface (device),
d4c1522
-	            priv->pending_actions->len,
d4c1522
+	            count,
d4c1522
 	            action);
d4c1522
 
d4c1522
-	if (priv->pending_actions->len == 1)
d4c1522
+	if (count == 1)
d4c1522
 		g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
d4c1522
 }
d4c1522
 
d4c1522
 /**
d4c1522
  * nm_device_remove_pending_action():
d4c1522
  * @device: the #NMDevice to remove the pending action from
d4c1522
  * @action: a static string that identifies the action
d4c1522
  *
d4c1522
  * Removes a pending action previously added by nm_device_add_pending_action().
d4c1522
  */
d4c1522
 void
d4c1522
 nm_device_remove_pending_action (NMDevice *device, const char *action)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
-	GQuark qaction;
d4c1522
-	guint i;
d4c1522
+	GSList *iter;
d4c1522
 
d4c1522
-	qaction = g_quark_from_static_string (action);
d4c1522
+	g_return_if_fail (action);
d4c1522
 
d4c1522
 	/* Shouldn't ever add the same pending action twice */
d4c1522
-	for (i = 0; i < priv->pending_actions->len; i++) {
d4c1522
-		if (qaction == g_array_index (priv->pending_actions, GQuark, i)) {
d4c1522
-			g_array_remove_index_fast (priv->pending_actions, i);
d4c1522
+	for (iter = priv->pending_actions; iter; iter = iter->next) {
d4c1522
+		if (!strcmp (action, iter->data)) {
d4c1522
+			g_free (iter->data);
d4c1522
+			priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter);
d4c1522
 			nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'",
d4c1522
 			            nm_device_get_iface (device),
d4c1522
-			            priv->pending_actions->len,
d4c1522
+			            g_slist_length (priv->pending_actions),
d4c1522
 			            action);
d4c1522
 
d4c1522
-			if (priv->pending_actions->len == 0)
d4c1522
+			if (priv->pending_actions == NULL)
d4c1522
 				g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
d4c1522
 			return;
d4c1522
 		}
d4c1522
 	}
d4c1522
 
d4c1522
 	nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' never added",
d4c1522
 	             nm_device_get_iface (device),
d4c1522
-	             priv->pending_actions->len,
d4c1522
+	             g_slist_length (priv->pending_actions),
d4c1522
 	             action);
d4c1522
+	g_return_if_reached ();
d4c1522
 }
d4c1522
 
d4c1522
 gboolean
d4c1522
 nm_device_has_pending_action (NMDevice *device)
d4c1522
 {
d4c1522
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
d4c1522
 
d4c1522
-	return priv->pending_actions->len > 0;
d4c1522
+	return !!priv->pending_actions;
d4c1522
 }
d4c1522
-- 
d4c1522
1.8.3.1
d4c1522
d4c1522
From b6ef165cfefba44a496cc336e1f91bb93b28f3ff Mon Sep 17 00:00:00 2001
d4c1522
From: Thomas Haller <thaller@redhat.com>
d4c1522
Date: Mon, 16 Dec 2013 15:02:38 +0100
d4c1522
Subject: [PATCH 2/2] core: fix NMActiveConnection to properly add/remove
d4c1522
 pending action "activation"
d4c1522
d4c1522
When a new activation request is received, NetworkManager creates a new
d4c1522
NMActiveConnection to track that request.  The device may already be activated,
d4c1522
in which case NetworkManager stops the old activation and starts the new one,
d4c1522
but both exist in parallel for a short period of time.  If the old
d4c1522
NMActiveConnection is activating and already has a pending 'activation'
d4c1522
action, when the new NMActiveConnection adds its own 'activating' action,
d4c1522
they will clash.  This is fixed by making each NMActiveConnection's activation
d4c1522
pending action uniquely named.
d4c1522
d4c1522
This fixes a g_warning with the following back trace:
d4c1522
d4c1522
  #0  0x000000328224edfd in g_logv () from /lib64/libglib-2.0.so.0
d4c1522
  #1  0x000000328224efe2 in g_log () from /lib64/libglib-2.0.so.0
d4c1522
  #2  0x000000328224f2e6 in g_warn_message () from /lib64/libglib-2.0.so.0
d4c1522
  #3  0x0000000000440aee in nm_device_add_pending_action (device=0x14002e0, action=0x50704a "activation") at devices/nm-device.c:7172
d4c1522
  #4  0x000000000047525c in nm_active_connection_set_device (self=0x141b3c0, device=0x14002e0) at nm-active-connection.c:364
d4c1522
  #5  0x0000000000475ec1 in set_property (object=0x141b3c0, prop_id=11, value=0x7fff7ff36c20, pspec=0x1405f70) at nm-active-connection.c:647
d4c1522
  #6  0x0000003282615d3e in g_object_newv () from /lib64/libgobject-2.0.so.0
d4c1522
  #7  0x00000032826162e6 in g_object_new_valist () from /lib64/libgobject-2.0.so.0
d4c1522
  #8  0x0000003282616654 in g_object_new () from /lib64/libgobject-2.0.so.0
d4c1522
  #9  0x0000000000474193 in nm_act_request_new (connection=0x13bb0e0, specific_object=0x0, subject=0x1447740, device=0x14002e0) at nm-activation-request.c:376
d4c1522
  #10 0x000000000048e477 in _new_active_connection (self=0x13e8060, connection=0x13bb0e0, specific_object=0x0, device=0x14002e0, subject=0x1447740, error=0x7fff7ff36f90) at nm-manager.c:2947
d4c1522
  #11 0x000000000048ed77 in impl_manager_activate_connection (self=0x13e8060, connection_path=0x134d590 "/org/freedesktop/NetworkManager/Settings/9", device_path=0x134d550 "/org/freedesktop/NetworkManager/Devices/1",
d4c1522
      specific_object_path=0x0, context=0x143a9b0) at nm-manager.c:3206
d4c1522
  #12 0x00000000004876c8 in dbus_glib_marshal_nm_manager_VOID__BOXED_BOXED_BOXED_POINTER (closure=0x7fff7ff37220, return_value=0x0, n_param_values=5, param_values=0x1448830, invocation_hint=0x0,
d4c1522
      marshal_data=0x48e9dd <impl_manager_activate_connection>) at nm-manager-glue.h:189
d4c1522
  #13 0x0000003284a0d6a9 in object_registration_message () from /lib64/libdbus-glib-1.so.2
d4c1522
  #14 0x000000348ea1ce66 in _dbus_object_tree_dispatch_and_unlock () from /lib64/libdbus-1.so.3
d4c1522
  #15 0x000000348ea0fa31 in dbus_connection_dispatch () from /lib64/libdbus-1.so.3
d4c1522
  #16 0x0000003284a0acc5 in message_queue_dispatch () from /lib64/libdbus-glib-1.so.2
d4c1522
  #17 0x0000003282247df6 in g_main_context_dispatch () from /lib64/libglib-2.0.so.0
d4c1522
  #18 0x0000003282248148 in g_main_context_iterate.isra.22 () from /lib64/libglib-2.0.so.0
d4c1522
  #19 0x000000328224854a in g_main_loop_run () from /lib64/libglib-2.0.so.0
d4c1522
  #20 0x000000000042c6c0 in main (argc=1, argv=0x7fff7ff379b8) at main.c:629
d4c1522
d4c1522
Signed-off-by: Thomas Haller <thaller@redhat.com>
d4c1522
---
d4c1522
 src/nm-active-connection.c | 17 ++++++++++++++---
d4c1522
 1 file changed, 14 insertions(+), 3 deletions(-)
d4c1522
d4c1522
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
d4c1522
index cb42417..a8a422c 100644
d4c1522
--- a/src/nm-active-connection.c
d4c1522
+++ b/src/nm-active-connection.c
d4c1522
@@ -42,14 +42,16 @@ G_DEFINE_ABSTRACT_TYPE (NMActiveConnection, nm_active_connection, G_TYPE_OBJECT)
d4c1522
 	NMDevice *device;
d4c1522
 
d4c1522
 	gboolean is_default;
d4c1522
 	gboolean is_default6;
d4c1522
 	NMActiveConnectionState state;
d4c1522
 	gboolean vpn;
d4c1522
 
d4c1522
+	char *pending_activation_id;
d4c1522
+
d4c1522
 	gboolean user_requested;
d4c1522
 	gulong user_uid;
d4c1522
 	NMDevice *master;
d4c1522
 } NMActiveConnectionPrivate;
d4c1522
 
d4c1522
 enum {
d4c1522
 	PROP_0,
d4c1522
@@ -138,16 +140,20 @@ nm_active_connection_set_state (NMActiveConnection *self,
d4c1522
 	    || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
d4c1522
 		nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (priv->connection),
d4c1522
 		                                         (guint64) time (NULL), TRUE);
d4c1522
 	}
d4c1522
 
d4c1522
 	if (priv->device) {
d4c1522
 		if (   old_state < NM_ACTIVE_CONNECTION_STATE_ACTIVATED
d4c1522
-		    && new_state >= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
d4c1522
-			nm_device_remove_pending_action (priv->device, "activation");
d4c1522
+		    && new_state >= NM_ACTIVE_CONNECTION_STATE_ACTIVATED &&
d4c1522
+		    priv->pending_activation_id)
d4c1522
+		{
d4c1522
+			nm_device_remove_pending_action (priv->device, priv->pending_activation_id);
d4c1522
+			g_clear_pointer (&priv->pending_activation_id, g_free);
d4c1522
+		}
d4c1522
 	}
d4c1522
 
d4c1522
 	if (priv->state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
d4c1522
 		/* Device is no longer relevant when deactivated */
d4c1522
 		g_clear_object (&priv->device);
d4c1522
 		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEVICES);
d4c1522
 	}
d4c1522
@@ -357,15 +363,16 @@ set_property (GObject *object, guint prop_id,
d4c1522
 		priv->connection = g_value_dup_object (value);
d4c1522
 		break;
d4c1522
 	case PROP_INT_DEVICE:
d4c1522
 		g_warn_if_fail (priv->device == NULL);
d4c1522
 		priv->device = g_value_dup_object (value);
d4c1522
 		if (priv->device) {
d4c1522
 			g_warn_if_fail (priv->device != priv->master);
d4c1522
-			nm_device_add_pending_action (priv->device, "activation");
d4c1522
+			priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)object);
d4c1522
+			nm_device_add_pending_action (priv->device, priv->pending_activation_id);
d4c1522
 		}
d4c1522
 		break;
d4c1522
 	case PROP_INT_USER_REQUESTED:
d4c1522
 		priv->user_requested = g_value_get_boolean (value);
d4c1522
 		break;
d4c1522
 	case PROP_INT_USER_UID:
d4c1522
 		priv->user_uid = g_value_get_ulong (value);
d4c1522
@@ -732,14 +739,18 @@ static void
d4c1522
 
d4c1522
 	g_free (priv->path);
d4c1522
 	priv->path = NULL;
d4c1522
 	g_free (priv->specific_object);
d4c1522
 	priv->specific_object = NULL;
d4c1522
 
d4c1522
 	g_clear_object (&priv->connection);
d4c1522
+	if (priv->pending_activation_id) {
d4c1522
+		nm_device_remove_pending_action (priv->device, priv->pending_activation_id);
d4c1522
+		g_clear_pointer (&priv->pending_activation_id, g_free);
d4c1522
+	}
d4c1522
 	g_clear_object (&priv->device);
d4c1522
 	g_clear_object (&priv->master);
d4c1522
 
d4c1522
 	G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object);
d4c1522
 }
d4c1522
 
d4c1522
 static void
d4c1522
-- 
d4c1522
1.8.3.1
d4c1522