about summary refs log tree commit diff
path: root/libnm-glib/nm-remote-settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-glib/nm-remote-settings.c')
-rw-r--r--libnm-glib/nm-remote-settings.c138
1 files changed, 90 insertions, 48 deletions
diff --git a/libnm-glib/nm-remote-settings.c b/libnm-glib/nm-remote-settings.c
index 8f213f9e..19d865c0 100644
--- a/libnm-glib/nm-remote-settings.c
+++ b/libnm-glib/nm-remote-settings.c
@@ -61,8 +61,6 @@ typedef struct {
 	DBusGProxy *dbus_proxy;
 
 	guint fetch_id;
-
-	gboolean disposed;
 } NMRemoteSettingsPrivate;
 
 enum {
@@ -109,7 +107,7 @@ static void
 _nm_remote_settings_ensure_inited (NMRemoteSettings *self)
 {
 	NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
-	GError *error;
+	GError *error = NULL;
 
 	if (!priv->inited) {
 		if (!g_initable_init (G_INITABLE (self), NULL, &error)) {
@@ -258,6 +256,43 @@ connection_removed_cb (NMRemoteConnection *remote, gpointer user_data)
 	g_hash_table_remove (priv->pending, path);
 }
 
+static void connection_visible_cb (NMRemoteConnection *remote,
+                                   gboolean visible,
+                                   gpointer user_data);
+
+/* Takes a reference to the connection when adding to 'to' */
+static void
+move_connection (NMRemoteSettings *self,
+                 NMRemoteConnection *remote,
+                 GHashTable *from,
+                 GHashTable *to)
+{
+	const char *path = nm_connection_get_path (NM_CONNECTION (remote));
+
+	g_hash_table_insert (to, g_strdup (path), g_object_ref (remote));
+	if (from)
+		g_hash_table_remove (from, path);
+
+	/* Setup connection signals since removing from 'from' clears them, but
+	 * also the first time the connection is added to a hash if 'from' is NULL.
+	 */
+	if (!g_signal_handler_find (remote, G_SIGNAL_MATCH_FUNC,
+	                            0, 0, NULL, connection_removed_cb, NULL)) {
+		g_signal_connect (remote,
+		                  NM_REMOTE_CONNECTION_REMOVED,
+		                  G_CALLBACK (connection_removed_cb),
+		                  self);
+	}
+
+	if (!g_signal_handler_find (remote, G_SIGNAL_MATCH_FUNC,
+	                            0, 0, NULL, connection_visible_cb, NULL)) {
+		g_signal_connect (remote,
+		                  "visible",
+		                  G_CALLBACK (connection_visible_cb),
+		                  self);
+	}
+}
+
 static void
 connection_visible_cb (NMRemoteConnection *remote,
                        gboolean visible,
@@ -278,16 +313,14 @@ connection_visible_cb (NMRemoteConnection *remote,
 		/* Connection visible to this user again */
 		if (g_hash_table_lookup (priv->pending, path)) {
 			/* Move connection from pending to visible hash; emit for clients */
-			g_hash_table_insert (priv->connections, g_strdup (path), g_object_ref (remote));
-			g_hash_table_remove (priv->pending, path);
+			move_connection (self, remote, priv->pending, priv->connections);
 			g_signal_emit (self, signals[NEW_CONNECTION], 0, remote);
 		}
 	} else {
 		/* Connection now invisible to this user */
 		if (g_hash_table_lookup (priv->connections, path)) {
 			/* Move connection to pending hash and wait for it to become visible again */
-			g_hash_table_insert (priv->pending, g_strdup (path), g_object_ref (remote));
-			g_hash_table_remove (priv->connections, path);
+			move_connection (self, remote, priv->connections, priv->pending);
 
 			/* Signal to clients that the connection is gone; but we have to
 			 * block our connection removed handler so we don't destroy
@@ -308,17 +341,14 @@ connection_inited (GObject *source, GAsyncResult *result, gpointer user_data)
 	NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
 	AddConnectionInfo *addinfo;
 	const char *path;
-	GError *error = NULL;
-	gboolean remove_from_pending = TRUE;
+	GError *error = NULL, *local;
 
 	path = nm_connection_get_path (NM_CONNECTION (remote));
 	addinfo = add_connection_info_find (self, remote);
 
 	if (g_async_initable_init_finish (G_ASYNC_INITABLE (remote), result, &error)) {
-		/* ref it when adding to ->connections, since removing it from ->pending
-		 * will unref it.
-		 */
-		g_hash_table_insert (priv->connections, g_strdup (path), g_object_ref (remote));
+		/* Connection is initialized and visible; expose it to clients */
+		move_connection (self, remote, priv->pending, priv->connections);
 
 		/* If there's a pending AddConnection request, complete that here before
 		 * signaling new-connection.
@@ -331,23 +361,23 @@ connection_inited (GObject *source, GAsyncResult *result, gpointer user_data)
 		 */
 		g_signal_emit (self, signals[NEW_CONNECTION], 0, remote);
 	} else {
-		if (dbus_g_error_has_name (error, "org.freedesktop.NetworkManager.Settings.PermissionDenied")) {
-			/* Connection doesn't exist, or isn't visible to this user */
-			remove_from_pending = FALSE;
-		}
-		g_error_free (error);
-
 		if (addinfo) {
-			error = g_error_new_literal (NM_REMOTE_SETTINGS_ERROR,
+			local = g_error_new_literal (NM_REMOTE_SETTINGS_ERROR,
 			                             NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE,
 			                             "Connection not visible or not available");
-			add_connection_info_complete (self, addinfo, error);
-			g_error_free (error);
+			add_connection_info_complete (self, addinfo, local);
+			g_error_free (local);
 		}
-	}
 
-	if (remove_from_pending)
-		g_hash_table_remove (priv->pending, path);
+		/* PermissionDenied means the connection isn't visible to this user, so
+		 * keep it in priv->pending to be notified later of visibility changes.
+		 * Otherwise forget it.
+		 */
+		if (!dbus_g_error_has_name (error, "org.freedesktop.NetworkManager.Settings.PermissionDenied"))
+			g_hash_table_remove (priv->pending, path);
+
+		g_error_free (error);
+	}
 
 	/* Let listeners know that all connections have been found */
 	priv->init_left--;
@@ -373,14 +403,6 @@ new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data)
 	/* Create a new connection object for it */
 	connection = nm_remote_connection_new (priv->bus, path);
 	if (connection) {
-		g_signal_connect (connection, NM_REMOTE_CONNECTION_REMOVED,
-		                  G_CALLBACK (connection_removed_cb),
-		                  self);
-
-		g_signal_connect (connection, "visible",
-		                  G_CALLBACK (connection_visible_cb),
-		                  self);
-
 		g_async_initable_init_async (G_ASYNC_INITABLE (connection),
 		                             G_PRIORITY_DEFAULT, NULL,
 		                             connection_inited, self);
@@ -389,7 +411,8 @@ new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data)
 		 * it's settings asynchronously over D-Bus.  The connection isn't
 		 * really valid until it has all its settings, so hide it until it does.
 		 */
-		g_hash_table_insert (priv->pending, g_strdup (path), connection);
+		move_connection (self, connection, NULL, priv->pending);
+		g_object_unref (connection); /* move_connection() takes a ref */
 	}
 	return connection;
 }
@@ -796,14 +819,25 @@ nm_remote_settings_new_finish (GAsyncResult *result, GError **error)
 		return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
 }
 
+static void
+forget_connection (gpointer user_data)
+{
+	NMRemoteConnection *remote = NM_REMOTE_CONNECTION (user_data);
+
+	g_signal_handlers_disconnect_matched (remote, G_SIGNAL_MATCH_FUNC,
+	                                      0, 0, NULL, connection_removed_cb, NULL);
+	g_signal_handlers_disconnect_matched (remote, G_SIGNAL_MATCH_FUNC,
+	                                      0, 0, NULL, connection_visible_cb, NULL);
+	g_object_unref (remote);
+}
 
 static void
 nm_remote_settings_init (NMRemoteSettings *self)
 {
 	NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
 
-	priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
-	priv->pending = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+	priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, forget_connection);
+	priv->pending = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, forget_connection);
 }
 
 static void
@@ -1007,29 +1041,35 @@ dispose (GObject *object)
 	NMRemoteSettings *self = NM_REMOTE_SETTINGS (object);
 	NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
 
-	if (priv->disposed)
-		return;
-
-	priv->disposed = TRUE;
-
-	if (priv->fetch_id)
+	if (priv->fetch_id) {
 		g_source_remove (priv->fetch_id);
+		priv->fetch_id = 0;
+	}
 
 	while (g_slist_length (priv->add_list))
 		add_connection_info_dispose (self, (AddConnectionInfo *) priv->add_list->data);
 
-	if (priv->connections)
+	if (priv->connections) {
 		g_hash_table_destroy (priv->connections);
+		priv->connections = NULL;
+	}
 
-	if (priv->pending)
+	if (priv->pending) {
 		g_hash_table_destroy (priv->pending);
+		priv->pending = NULL;
+	}
 
 	g_free (priv->hostname);
+	priv->hostname = NULL;
 
-	g_object_unref (priv->dbus_proxy);
-	g_object_unref (priv->proxy);
-	g_object_unref (priv->props_proxy);
-	dbus_g_connection_unref (priv->bus);
+	g_clear_object (&priv->dbus_proxy);
+	g_clear_object (&priv->proxy);
+	g_clear_object (&priv->props_proxy);
+
+	if (priv->bus) {
+		dbus_g_connection_unref (priv->bus);
+		priv->bus = NULL;
+	}
 
 	G_OBJECT_CLASS (nm_remote_settings_parent_class)->dispose (object);
 }
@@ -1061,6 +1101,8 @@ get_property (GObject *object, guint prop_id,
 {
 	NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object);
 
+	_nm_remote_settings_ensure_inited (NM_REMOTE_SETTINGS (object));
+
 	switch (prop_id) {
 	case PROP_BUS:
 		g_value_set_boxed (value, priv->bus);