summary refs log tree commit diff
path: root/src/settings/nm-agent-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings/nm-agent-manager.c')
-rw-r--r--src/settings/nm-agent-manager.c82
1 files changed, 69 insertions, 13 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index 531fabc5..d3635706 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -74,7 +74,7 @@ static void request_add_agent (Request *req,
                                NMSecretAgent *agent,
                                NMSessionMonitor *session_monitor);
 
-static void request_remove_agent (Request *req, NMSecretAgent *agent);
+static void request_remove_agent (gpointer key, gpointer value, gpointer user_data);
 
 static void impl_agent_manager_register (NMAgentManager *self,
                                          const char *identifier,
@@ -101,13 +101,60 @@ nm_agent_manager_error_quark (void)
 
 /*************************************************************/
 
+/*----------------------------------------------------------------------------*/
+/* GHashTable safe iterating function: x_g_hash_table_safe_for_each()
+ * GHashTable can't be modified while iterating, the common solution for that is
+ * to flatten the hash table first and iterate over list.
+ * Taken from https://github.com/linuxmint/nemo/blob/master/eel/eel-glib-extensions.c
+ */
+typedef struct {
+	GList *keys;
+	GList *values;
+} FlattenedHashTable;
+
+static void
+flatten_hash_table_element (gpointer key, gpointer value, gpointer callback_data)
+{
+	FlattenedHashTable *flattened_table;
+
+	flattened_table = callback_data;
+	flattened_table->keys = g_list_prepend
+	        (flattened_table->keys, key);
+	flattened_table->values = g_list_prepend
+	        (flattened_table->values, value);
+}
+
+static void
+x_g_hash_table_safe_for_each (GHashTable *hash_table,
+                              GHFunc callback,
+                              gpointer callback_data)
+{
+	FlattenedHashTable flattened;
+	GList *p, *q;
+
+	flattened.keys = NULL;
+	flattened.values = NULL;
+
+	g_hash_table_foreach (hash_table,
+	                      flatten_hash_table_element,
+	                      &flattened);
+
+	for (p = flattened.keys, q = flattened.values;
+	     p != NULL;
+	     p = p->next, q = q->next) {
+	        (* callback) (p->data, q->data, callback_data);
+	}
+
+	g_list_free (flattened.keys);
+	g_list_free (flattened.values);
+}
+/*----------------------------------------------------------------------------*/
+
 static gboolean
 remove_agent (NMAgentManager *self, const char *owner)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
 	NMSecretAgent *agent;
-	GHashTableIter iter;
-	gpointer data;
 
 	g_return_val_if_fail (owner != NULL, FALSE);
 
@@ -119,10 +166,8 @@ remove_agent (NMAgentManager *self, const char *owner)
 	nm_log_dbg (LOGD_AGENTS, "(%s) agent unregistered",
 	            nm_secret_agent_get_description (agent));
 
-	/* Remove this agent to any in-progress secrets requests */
-	g_hash_table_iter_init (&iter, priv->requests);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		request_remove_agent ((Request *) data, agent);
+	/* Remove this agent from any in-progress secrets requests */
+	x_g_hash_table_safe_for_each (priv->requests, request_remove_agent, agent);
 
 	/* And dispose of the agent */
 	g_hash_table_remove (priv->agents, owner);
@@ -471,7 +516,7 @@ request_free (Request *req)
 	if (req->cancel_callback)
 		req->cancel_callback (req);
 
-	g_slist_free (req->pending);
+	g_slist_free_full (req->pending, g_object_unref);
 	g_slist_free (req->asked);
 	g_object_unref (req->connection);
 	g_free (req->setting_name);
@@ -576,7 +621,7 @@ request_add_agent (Request *req,
 
 	/* Add this agent to the list, preferring active sessions */
 	req->pending = g_slist_insert_sorted_with_data (req->pending,
-	                                                agent,
+	                                                g_object_ref (agent),
 	                                                (GCompareDataFunc) agent_compare_func,
 	                                                session_monitor);
 }
@@ -594,17 +639,21 @@ request_add_agents (NMAgentManager *self, Request *req)
 }
 
 static void
-request_remove_agent (Request *req, NMSecretAgent *agent)
+request_remove_agent (gpointer key, gpointer value, gpointer user_data)
 {
+	Request *req = (Request *) value;
+	NMSecretAgent *agent = (NMSecretAgent *) user_data;
 	gboolean try_next = FALSE;
 	const char *detail = "";
+	GSList *found;
 
 	g_return_if_fail (req != NULL);
 	g_return_if_fail (agent != NULL);
 
 	/* If this agent is being asked right now, cancel the request */
 	if (agent == req->current) {
-		req->cancel_callback (req);
+		if (req->cancel_callback)
+			req->cancel_callback (req);
 		req->current_has_modify = FALSE;
 		req->current = NULL;
 		req->current_call_id = NULL;
@@ -616,7 +665,12 @@ request_remove_agent (Request *req, NMSecretAgent *agent)
 				nm_secret_agent_get_description (agent),
 				detail, req, req->setting_name);
 
-	req->pending = g_slist_remove (req->pending, agent);
+	found = g_slist_find (req->pending, agent);
+	if (found) {
+		req->pending = g_slist_remove_link (req->pending, found);
+		g_object_unref (found->data);
+		g_slist_free_1 (found);
+	}
 
 	if (try_next) {
 		/* If the agent serving the in-progress secrets request went away then
@@ -642,6 +696,8 @@ next_generic (Request *req, const char *detail)
 	} else {
 		/* Send a secrets request to the next agent */
 		req->current_has_modify = FALSE;
+		if (req->current)
+			g_object_unref (req->current);
 		req->current = req->pending->data;
 		req->pending = g_slist_remove (req->pending, req->current);
 
@@ -1452,7 +1508,7 @@ dispose (GObject *object)
 
 		nm_auth_changed_func_unregister (authority_changed_cb, NM_AGENT_MANAGER (object));
 
-		g_slist_foreach (priv->chains, (GFunc) nm_auth_chain_unref, NULL);
+		g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref);
 
 		g_hash_table_destroy (priv->agents);
 		g_hash_table_destroy (priv->requests);