From 2e720a1dc8b488969d80150487a733d462ccfacb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 24 Jun 2021 21:47:15 +0200 Subject: [PATCH] settings: prune old entries from keyfile databases We have two GKeyfile files (timestamps and seen-bssids). When a profile was deleted while NetworkManager was running, then entries were removed from these keyfiles. But if a profile disappeared while NetworkManger was stopped, then those UUIDs piled up. This also happens if you have temporary connections in /run and reboot. We need a way to garbage collect entries that are no longer relevant. As the keyfile databases only get loaded once from disk, we will prune all UUIDs for which we have no more connection loaded, on the first time we write out the files again. Note what this means: if you "temporarily" remove a connection profile (without NetworkManager noticing) and restore it later, then the additional information might have been pruned. There is no way how NetworkManager could know that this UUID is coming back. The alternative is what we did before: pile them up indefinitely. That seems more problematic. --- src/core/settings/nm-settings.c | 49 ++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c index ee6ac59314..084d6f86aa 100644 --- a/src/core/settings/nm-settings.c +++ b/src/core/settings/nm-settings.c @@ -388,6 +388,9 @@ typedef struct { guint kf_db_flush_idle_id_timestamps; guint kf_db_flush_idle_id_seen_bssids; + bool kf_db_pruned_timestamps; + bool kf_db_pruned_seen_bssid; + bool started : 1; /* Whether NMSettingsConnections changed in a way that affects the comparison @@ -3684,6 +3687,37 @@ again: /*****************************************************************************/ +static gboolean +_kf_db_prune_predicate(const char *uuid, gpointer user_data) +{ + return !!nm_settings_get_connection_by_uuid(user_data, uuid); +} + +static void +_kf_db_to_file(NMSettings *self, gboolean is_timestamps, gboolean force_write) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self); + NMKeyFileDB * kf_db; + bool * p_kf_db_pruned; + + if (is_timestamps) { + kf_db = priv->kf_db_timestamps; + p_kf_db_pruned = &priv->kf_db_pruned_timestamps; + } else { + kf_db = priv->kf_db_seen_bssids; + p_kf_db_pruned = &priv->kf_db_pruned_seen_bssid; + } + + if (!*p_kf_db_pruned) { + /* we only prune the DB once, because afterwards every + * add/remove of an connection will lead to a direct update. */ + *p_kf_db_pruned = TRUE; + nm_key_file_db_prune(kf_db, _kf_db_prune_predicate, self); + } + + nm_key_file_db_to_file(kf_db, force_write); +} + G_GNUC_PRINTF(4, 5) static void _kf_db_log_fcn(NMKeyFileDB *kf_db, int syslog_level, gpointer user_data, const char *fmt, ...) @@ -3732,7 +3766,7 @@ _kf_db_got_dirty_flush(NMSettings *self, gboolean is_timestamps) } if (nm_key_file_db_is_dirty(kf_db)) - nm_key_file_db_to_file(kf_db, FALSE); + _kf_db_to_file(self, is_timestamps, FALSE); else { _LOGT("[%s-keyfile]: skip saving changes to \"%s\"", prefix, @@ -3785,15 +3819,10 @@ _kf_db_got_dirty_fcn(NMKeyFileDB *kf_db, gpointer user_data) void nm_settings_kf_db_write(NMSettings *self) { - NMSettingsPrivate *priv; - g_return_if_fail(NM_IS_SETTINGS(self)); - priv = NM_SETTINGS_GET_PRIVATE(self); - if (priv->kf_db_timestamps) - nm_key_file_db_to_file(priv->kf_db_timestamps, TRUE); - if (priv->kf_db_seen_bssids) - nm_key_file_db_to_file(priv->kf_db_seen_bssids, TRUE); + _kf_db_to_file(self, TRUE, TRUE); + _kf_db_to_file(self, FALSE, TRUE); } /*****************************************************************************/ @@ -4031,8 +4060,8 @@ finalize(GObject *object) nm_clear_g_source(&priv->kf_db_flush_idle_id_timestamps); nm_clear_g_source(&priv->kf_db_flush_idle_id_seen_bssids); - nm_key_file_db_to_file(priv->kf_db_timestamps, FALSE); - nm_key_file_db_to_file(priv->kf_db_seen_bssids, FALSE); + _kf_db_to_file(self, TRUE, FALSE); + _kf_db_to_file(self, FALSE, FALSE); nm_key_file_db_destroy(priv->kf_db_timestamps); nm_key_file_db_destroy(priv->kf_db_seen_bssids);