diff options
author | Thomas Haller <thaller@redhat.com> | 2019-07-01 18:28:48 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-07-08 14:23:15 +0200 |
commit | 852e0c5ee2ec9b2ac92401707a6ab8e2fd92c1d4 (patch) | |
tree | 91cc6b3cc83087153e8e2ca47b9a0eac3e2ce1ef | |
parent | e356fde636324f3fbb4ff3387dc6af1c9748517c (diff) | |
download | NetworkManager-th/settings-delegate-storage-1.tar.gz |
fixup! settings: rework tracking settings connections and settings pluginsth/settings-delegate-storage-1
21 files changed, 2171 insertions, 1806 deletions
diff --git a/Makefile.am b/Makefile.am index 29d485d06d..eed72db3c8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2098,6 +2098,8 @@ src_libNetworkManager_la_SOURCES = \ src/settings/nm-settings-plugin.h \ src/settings/nm-settings.c \ src/settings/nm-settings.h \ + src/settings/nm-settings-utils.c \ + src/settings/nm-settings-utils.h \ \ src/settings/plugins/keyfile/nms-keyfile-storage.c \ src/settings/plugins/keyfile/nms-keyfile-storage.h \ @@ -2819,6 +2821,8 @@ $(src_settings_plugins_ifcfg_rh_libnms_ifcfg_rh_core_la_OBJECTS): $(libnm_core_l ############################################################################### src_settings_plugins_ifcfg_rh_libnm_settings_plugin_ifcfg_rh_la_SOURCES = \ + src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c \ + src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h \ src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c \ src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h \ $(NULL) diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 937b9b5eb1..ee21fef542 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -601,7 +601,6 @@ nm_settings_connection_delete (NMSettingsConnection *self, nm_settings_delete_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings, self, - TRUE, allow_add_to_no_auto_default); } diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c index e64cc84f64..8ae1e6528a 100644 --- a/src/settings/nm-settings-plugin.c +++ b/src/settings/nm-settings-plugin.c @@ -70,7 +70,7 @@ nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self) void nm_settings_plugin_reload_connections (NMSettingsPlugin *self, - NMSettingsPluginConnectionReloadCallback callback, + NMSettingsPluginConnectionLoadCallback callback, gpointer user_data) { NMSettingsPluginClass *klass; @@ -83,29 +83,59 @@ nm_settings_plugin_reload_connections (NMSettingsPlugin *self, klass->reload_connections (self, callback, user_data); } -gboolean -nm_settings_plugin_load_connection (NMSettingsPlugin *self, - const char *filename, - NMSettingsStorage **out_storage, - NMConnection **out_connection, - NMSettingsStorage **out_storage_replaced, - GError **error) +NMSettingsPluginConnectionLoadEntry * +nm_settings_plugin_create_connection_load_entries (const char *const*filenames, + gsize *out_len) +{ + NMSettingsPluginConnectionLoadEntry *entries; + gsize len; + gsize i; + + len = NM_PTRARRAY_LEN (filenames); + if (len == 0) { + *out_len = 0; + return NULL; + } + + entries = g_new (NMSettingsPluginConnectionLoadEntry, len); + for (i = 0; i < len; i++) { + entries[i] = (NMSettingsPluginConnectionLoadEntry) { + .filename = filenames[i], + .error = NULL, + .handled = FALSE, + }; + } + + *out_len = len; + return entries; +} + +void +nm_settings_plugin_load_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { NMSettingsPluginClass *klass; - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE); - g_return_val_if_fail (filename && filename[0] == '/', FALSE); - g_return_val_if_fail (out_storage && !*out_storage, FALSE); - g_return_val_if_fail (out_connection && !*out_connection, FALSE); - g_return_val_if_fail (out_storage_replaced && !*out_storage_replaced, FALSE); + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); - if (!klass->load_connection) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "settings plugin does not support loading connection"); - return FALSE; - } - return klass->load_connection (self, filename, out_storage, out_connection, out_storage_replaced, error); + if (klass->load_connections) + klass->load_connections (self, entries, n_entries, callback, user_data); +} + +void +nm_settings_plugin_load_connections_done (NMSettingsPlugin *self) +{ + NMSettingsPluginClass *klass; + + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (klass->load_connections_done) + klass->load_connections_done (self); } gboolean @@ -183,7 +213,6 @@ nm_settings_plugin_update_connection (NMSettingsPlugin *self, gboolean nm_settings_plugin_delete_connection (NMSettingsPlugin *self, NMSettingsStorage *storage, - gboolean remove_from_disk, GError **error) { NMSettingsPluginClass *klass = NULL; @@ -204,7 +233,6 @@ nm_settings_plugin_delete_connection (NMSettingsPlugin *self, return klass->delete_connection (self, storage, - remove_from_disk, error); } diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h index a214d7c026..188614bed7 100644 --- a/src/settings/nm-settings-plugin.h +++ b/src/settings/nm-settings-plugin.h @@ -27,10 +27,16 @@ typedef struct _NMSettingsPlugin NMSettingsPlugin; -typedef void (*NMSettingsPluginConnectionReloadCallback) (NMSettingsPlugin *self, - NMSettingsStorage *storage, - NMConnection *connection, - gpointer user_data); +typedef void (*NMSettingsPluginConnectionLoadCallback) (NMSettingsPlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + gpointer user_data); + +typedef struct { + const char *filename; + GError *error; + bool handled:1; +} NMSettingsPluginConnectionLoadEntry; #define NM_TYPE_SETTINGS_PLUGIN (nm_settings_plugin_get_type ()) #define NM_SETTINGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPlugin)) @@ -72,23 +78,23 @@ typedef struct { */ GSList * (*get_unrecognized_specs) (NMSettingsPlugin *self); - /* Requests that the plugin load/reload a single connection, if it - * recognizes the filename. Returns success or failure. + /* Requests that the plugin load/reload a set of filenames. */ - gboolean (*load_connection) (NMSettingsPlugin *self, - const char *filename, - NMSettingsStorage **out_storage, - NMConnection **out_connection, - NMSettingsStorage **out_storage_replaced, - GError **error); + void (*load_connections) (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); /* Requests that the plugin reload all connection files from disk, * and emit signals reflecting new, changed, and removed connections. */ void (*reload_connections) (NMSettingsPlugin *self, - NMSettingsPluginConnectionReloadCallback callback, + NMSettingsPluginConnectionLoadCallback callback, gpointer user_data); + void (*load_connections_done) (NMSettingsPlugin *self); + gboolean (*add_connection) (NMSettingsPlugin *self, NMConnection *connection, NMSettingsStorage **out_storage, @@ -104,7 +110,6 @@ typedef struct { gboolean (*delete_connection) (NMSettingsPlugin *self, NMSettingsStorage *storage, - gboolean remove_from_disk, GError **error); const char *plugin_name; @@ -144,15 +149,19 @@ GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self); GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self); void nm_settings_plugin_reload_connections (NMSettingsPlugin *self, - NMSettingsPluginConnectionReloadCallback callback, + NMSettingsPluginConnectionLoadCallback callback, gpointer user_data); -gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *self, - const char *filename, - NMSettingsStorage **out_storage, - NMConnection **out_connection, - NMSettingsStorage **out_storage_replaced, - GError **error); +NMSettingsPluginConnectionLoadEntry *nm_settings_plugin_create_connection_load_entries (const char *const*filenames, + gsize *out_len); + +void nm_settings_plugin_load_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + +void nm_settings_plugin_load_connections_done (NMSettingsPlugin *self); gboolean nm_settings_plugin_add_connection (NMSettingsPlugin *self, NMConnection *connection, @@ -169,7 +178,6 @@ gboolean nm_settings_plugin_update_connection (NMSettingsPlugin *self, gboolean nm_settings_plugin_delete_connection (NMSettingsPlugin *self, NMSettingsStorage *storage, - gboolean remove_from_disk, GError **error); /*****************************************************************************/ diff --git a/src/settings/nm-settings-storage.c b/src/settings/nm-settings-storage.c index d07da2ca39..2d1303ddb3 100644 --- a/src/settings/nm-settings-storage.c +++ b/src/settings/nm-settings-storage.c @@ -48,8 +48,8 @@ nm_settings_storage_cmp (NMSettingsStorage *a, nm_assert (nm_streq (nm_settings_storage_get_uuid (a), nm_settings_storage_get_uuid (b))); /* in-memory has always higher priority */ - NM_CMP_DIRECT (nm_settings_storage_is_in_memory (a), - nm_settings_storage_is_in_memory (b)); + NM_CMP_DIRECT (nm_settings_storage_is_keyfile_run (a), + nm_settings_storage_is_keyfile_run (b)); plugin_a = nm_settings_storage_get_plugin (a); plugin_b = nm_settings_storage_get_plugin (b); @@ -58,6 +58,7 @@ nm_settings_storage_cmp (NMSettingsStorage *a, int idx_a = g_slist_index ((GSList *) plugin_list, plugin_a); int idx_b = g_slist_index ((GSList *) plugin_list, plugin_b); + /* the plugins must be found in the list. */ nm_assert (idx_a >= 0); nm_assert (idx_b >= 0); nm_assert (idx_a != idx_b); @@ -70,19 +71,17 @@ nm_settings_storage_cmp (NMSettingsStorage *a, return 0; } - /* a hidden storage (tombstone) has higher priority... */ - NM_CMP_DIRECT (nm_settings_storage_is_tombstone (a), - nm_settings_storage_is_tombstone (b)); - klass = NM_SETTINGS_STORAGE_GET_CLASS (a); if (klass != NM_SETTINGS_STORAGE_GET_CLASS (b)) { + /* one plugin must return storages of the same type. Otherwise, it's + * unclear how cmp_fcn() should compare them. */ nm_assert_not_reached (); return 0; } - if (klass->storage_cmp) - NM_CMP_RETURN (klass->storage_cmp (a, b)); + if (klass->cmp_fcn) + NM_CMP_RETURN (klass->cmp_fcn (a, b)); return 0; } @@ -99,54 +98,6 @@ G_DEFINE_TYPE (NMSettingsStorage, nm_settings_storage, G_TYPE_OBJECT) /*****************************************************************************/ -void -_nm_settings_storage_set_filename (NMSettingsStorage *self, - const char *filename) -{ - g_return_if_fail (NM_IS_SETTINGS_STORAGE (self)); - - if (nm_streq0 (self->_filename, filename)) - return; - - g_free (self->_filename); - self->_filename = g_strdup (filename); - _notify (self, PROP_FILENAME); -} - -void -_nm_settings_storage_set_filename_take (NMSettingsStorage *self, - char *filename) -{ - g_return_if_fail (NM_IS_SETTINGS_STORAGE (self)); - - if (nm_streq0 (self->_filename, filename)) { - g_free (filename); - return; - } - - g_free (self->_filename); - self->_filename = filename; - _notify (self, PROP_FILENAME); -} - -/*****************************************************************************/ - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMSettingsStorage *self = NM_SETTINGS_STORAGE (object); - - switch (prop_id) { - case PROP_FILENAME: - g_value_set_string (value, nm_settings_storage_get_filename (self)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -162,7 +113,7 @@ set_property (GObject *object, guint prop_id, case PROP_UUID: /* construct-only */ self->_uuid = g_value_dup_string (value); - nm_assert (nm_utils_is_uuid (self->_uuid)); + nm_assert (!self->_uuid || nm_utils_is_uuid (self->_uuid)); break; case PROP_FILENAME: /* construct-only */ @@ -179,6 +130,8 @@ set_property (GObject *object, guint prop_id, static void nm_settings_storage_init (NMSettingsStorage *self) { + c_list_init (&self->_storage_lst); + c_list_init (&self->_storage_by_uuid_lst); } NMSettingsStorage * @@ -201,6 +154,9 @@ finalize (GObject *object) { NMSettingsStorage *self = NM_SETTINGS_STORAGE (object); + c_list_unlink_stale (&self->_storage_lst); + c_list_unlink_stale (&self->_storage_by_uuid_lst); + g_object_unref (self->_plugin); g_free (self->_uuid); g_free (self->_filename); @@ -213,7 +169,6 @@ nm_settings_storage_class_init (NMSettingsStorageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; @@ -234,7 +189,7 @@ nm_settings_storage_class_init (NMSettingsStorageClass *klass) obj_properties[PROP_FILENAME] = g_param_spec_string (NM_SETTINGS_STORAGE_FILENAME, "", "", NULL, - G_PARAM_READWRITE | + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); diff --git a/src/settings/nm-settings-storage.h b/src/settings/nm-settings-storage.h index 7164a15753..d71bd0b556 100644 --- a/src/settings/nm-settings-storage.h +++ b/src/settings/nm-settings-storage.h @@ -22,6 +22,8 @@ /*****************************************************************************/ +#include "c-list/src/c-list.h" + #define NM_TYPE_SETTINGS_STORAGE (nm_settings_storage_get_type ()) #define NM_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorage)) #define NM_SETTINGS_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass)) @@ -40,13 +42,15 @@ typedef struct NMSettingsStorage { struct _NMSettingsPlugin *_plugin; char *_uuid; char *_filename; + CList _storage_lst; + CList _storage_by_uuid_lst; } NMSettingsStorage; typedef struct { GObjectClass parent; - int (*storage_cmp) (NMSettingsStorage *a, - NMSettingsStorage *b); + int (*cmp_fcn) (NMSettingsStorage *a, + NMSettingsStorage *b); } NMSettingsStorageClass; @@ -59,8 +63,11 @@ NMSettingsStorage *nm_settings_storage_new (struct _NMSettingsPlugin *plugin, static inline struct _NMSettingsPlugin * nm_settings_storage_get_plugin (NMSettingsStorage *self) { + GType nm_settings_plugin_get_type (void); + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + nm_assert (G_TYPE_CHECK_INSTANCE_TYPE (self->_plugin, nm_settings_plugin_get_type ())); return self->_plugin; } @@ -76,24 +83,22 @@ nm_settings_storage_get_uuid (NMSettingsStorage *self) } static inline const char * -nm_settings_storage_get_filename (NMSettingsStorage *self) +nm_settings_storage_get_uuid_opt (NMSettingsStorage *self) { + gboolean nm_utils_is_uuid (const char *str); + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); - return self->_filename; + nm_assert (!self->_uuid || nm_utils_is_uuid (self->_uuid)); + return self->_uuid; } -void _nm_settings_storage_set_filename (NMSettingsStorage *self, - const char *filename); -void _nm_settings_storage_set_filename_take (NMSettingsStorage *self, - char *filename); - -static inline gboolean -nm_settings_storage_is_tombstone (NMSettingsStorage *self) +static inline const char * +nm_settings_storage_get_filename (NMSettingsStorage *self) { - /* Tombstones are intended to hide a UUID. This is not yet implemented, - * nor used ATM. Maybe TODO, maybe this concept can be dropped... */ - return FALSE; + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + return self->_filename; } /*****************************************************************************/ diff --git a/src/settings/nm-settings-utils.c b/src/settings/nm-settings-utils.c new file mode 100644 index 0000000000..c88b2e430c --- /dev/null +++ b/src/settings/nm-settings-utils.c @@ -0,0 +1,245 @@ +/* NetworkManager system settings service - keyfile plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-settings-utils.h" + +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include "nm-settings-plugin.h" + +/*****************************************************************************/ + +const struct timespec * +nm_sett_util_stat_mtime (const char *filename, + gboolean do_lstat, + struct timespec *out_val) +{ + struct stat st; + struct timeval now_tv; + + if (filename) { + if (do_lstat) { + if (lstat (filename, &st) == 0) { + *out_val = st.st_mtim; + return out_val; + } + } else { + if (stat (filename, &st) == 0) { + *out_val = st.st_mtim; + return out_val; + } + } + } + + if (gettimeofday (&now_tv, NULL) == 0) { + *out_val = (struct timespec) { + .tv_sec = now_tv.tv_sec, + .tv_nsec = now_tv.tv_usec * 1000u, + }; + return out_val; + } + + *out_val = (struct timespec) { }; + return out_val; +} + +/*****************************************************************************/ + +void +nm_sett_util_storage_by_uuid_head_destroy (NMSettUtilStorageByUuidHead *sbuh) +{ + CList *iter; + + while ((iter = c_list_first (&sbuh->lst_by_uuid_head))) + c_list_unlink (iter); + g_free (sbuh); +} + +/*****************************************************************************/ + +#define STORAGES_INIT(storages) \ + { \ + .lst_head = C_LIST_INIT (((storages).lst_head)), \ + .idx_by_filename = g_hash_table_new_full (nm_str_hash, \ + g_str_equal, \ + NULL, \ + (GDestroyNotify) _storage_destroy), \ + .idx_by_uuid = g_hash_table_new_full (nm_pstr_hash, \ + nm_pstr_equal, \ + NULL, \ + (GDestroyNotify) _storage_by_uuid_head_destroy), \ + } + +void +nm_sett_util_storages_clear (NMSettUtilStorages *storages) +{ + nm_clear_pointer (&storages->idx_by_uuid, g_hash_table_destroy); + nm_clear_pointer (&storages->idx_by_filename, g_hash_table_destroy); + nm_assert (c_list_is_empty (&storages->lst_head)); +} + +#define nm_auto_clear_storages nm_auto(_storages_clear) + +void +nm_sett_util_storages_swap (NMSettUtilStorages *storages, + NMSettUtilStorages *other) +{ + GHashTable *tmp; + + c_list_swap (&storages->lst_head, &other->lst_head); + + tmp = storages->idx_by_uuid; + storages->idx_by_uuid = other->idx_by_uuid; + other->idx_by_uuid = tmp; + + tmp = storages->idx_by_filename; + storages->idx_by_filename = other->idx_by_filename; + other->idx_by_filename = tmp; +} + +void +nm_sett_util_storages_add_take (NMSettUtilStorages *storages, + gpointer storage_take_p /* NMSettingsStorage *, take reference */) +{ + NMSettingsStorage *storage_take = storage_take_p; + NMSettUtilStorageByUuidHead *sbuh; + const char *uuid; + + nm_assert (storage_take); + nm_assert (c_list_is_empty (&storage_take->_storage_lst)); + nm_assert (c_list_is_empty (&storage_take->_storage_by_uuid_lst)); + nm_assert (nm_settings_storage_get_filename (storage_take)); + + if (!g_hash_table_replace (storages->idx_by_filename, + (char *) nm_settings_storage_get_filename (storage_take), + storage_take /* takes ownership of reference. */)) + nm_assert_not_reached (); + + uuid = nm_settings_storage_get_uuid_opt (storage_take); + + if (uuid) { + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + if (!sbuh) { + gsize l = strlen (uuid) + 1; + + sbuh = g_malloc (sizeof (NMSettUtilStorageByUuidHead) + l); + sbuh->uuid = sbuh->uuid_data; + c_list_init (&sbuh->lst_by_uuid_head); + memcpy (sbuh->uuid_data, uuid, l); + g_hash_table_add (storages->idx_by_uuid, sbuh); + } + c_list_link_tail (&sbuh->lst_by_uuid_head, &storage_take->_storage_by_uuid_lst); + } + + c_list_link_tail (&storages->lst_head, &storage_take->_storage_lst); +} + +gpointer /* NMSettingsStorage * */ +nm_sett_util_storages_steal (NMSettUtilStorages *storages, + gpointer storage_p /* NMSettingsStorage **/) +{ + NMSettingsStorage *storage = storage_p; + NMSettUtilStorageByUuidHead *sbuh; + const char *uuid; + + nm_assert (storage); + nm_assert (nm_sett_util_storages_lookup_by_filename (storages, nm_settings_storage_get_filename (storage)) == storage); + nm_assert (c_list_contains (&storages->lst_head, &storage->_storage_lst)); + + uuid = nm_settings_storage_get_uuid_opt (storage); + + if (!uuid) { + nm_assert (c_list_is_empty (&storage->_storage_by_uuid_lst)); + } else { + nm_assert (!c_list_is_empty (&storage->_storage_by_uuid_lst)); + + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->lst_by_uuid_head, &storage->_storage_by_uuid_lst)); + c_list_unlink (&storage->_storage_by_uuid_lst); + + if (c_list_is_empty (&sbuh->lst_by_uuid_head)) + g_hash_table_remove (storages->idx_by_uuid, sbuh); + } + + c_list_unlink (&storage->_storage_lst); + + g_hash_table_steal (storages->idx_by_filename, nm_settings_storage_get_filename (storage)); + + return storage; +} + +gpointer /* NMSettingsStorage */ +nm_sett_util_storages_steal_and_replace (NMSettUtilStorages *storages, + gpointer storage_old_p /* NMSettingsStorage * */, + gpointer storage_new_p /* NMSettingsStorage * */) +{ + NMSettingsStorage *storage_old = storage_old_p; + NMSettingsStorage *storage_new = storage_new_p; + const char *uuid; + const char *filename; + NMSettUtilStorageByUuidHead *sbuh; + + nm_assert (storage_old); + nm_assert (storage_new); + + uuid = nm_settings_storage_get_uuid_opt (storage_old); + filename = nm_settings_storage_get_filename (storage_old); + + nm_assert (nm_streq0 (uuid, nm_settings_storage_get_uuid_opt (storage_new))); + nm_assert (filename && nm_streq0 (filename, nm_settings_storage_get_filename (storage_new))); + + nm_assert (c_list_is_empty (&storage_new->_storage_lst)); + nm_assert (c_list_is_empty (&storage_new->_storage_by_uuid_lst)); + nm_assert (nm_settings_storage_get_filename (storage_new)); + + nm_assert (nm_sett_util_storages_lookup_by_filename (storages, filename) == storage_old); + nm_assert (c_list_contains (&storages->lst_head, &storage_old->_storage_lst)); + + if (!uuid) { + nm_assert (c_list_is_empty (&storage_old->_storage_by_uuid_lst)); + } else { + nm_assert (!c_list_is_empty (&storage_old->_storage_by_uuid_lst)); + + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->lst_by_uuid_head, &storage_old->_storage_by_uuid_lst)); + + c_list_link_after (&storage_old->_storage_by_uuid_lst, &storage_new->_storage_by_uuid_lst); + c_list_unlink (&storage_old->_storage_by_uuid_lst); + } + + c_list_link_after (&storage_old->_storage_lst, &storage_new->_storage_lst); + c_list_unlink (&storage_old->_storage_lst); + + if (!g_hash_table_steal (storages->idx_by_filename, filename)) + nm_assert_not_reached (); + + if (!g_hash_table_insert (storages->idx_by_filename, (char *) nm_settings_storage_get_filename (storage_new), storage_new)) + nm_assert_not_reached (); + + return storage_old; +} diff --git a/src/settings/nm-settings-utils.h b/src/settings/nm-settings-utils.h new file mode 100644 index 0000000000..373ad3a9a7 --- /dev/null +++ b/src/settings/nm-settings-utils.h @@ -0,0 +1,101 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_SETTINGS_UTILS_H__ +#define __NM_SETTINGS_UTILS_H__ + +#include "nm-settings-storage.h" + +/*****************************************************************************/ + +struct timespec; + +const struct timespec *nm_sett_util_stat_mtime (const char *filename, + gboolean do_lstat, + struct timespec *out_val); + +/*****************************************************************************/ + +typedef struct { + const char *uuid; + + CList lst_by_uuid_head; + + char uuid_data[]; +} NMSettUtilStorageByUuidHead; + +typedef struct { + CList lst_head; + GHashTable *idx_by_filename; + GHashTable *idx_by_uuid; +} NMSettUtilStorages; + +void nm_sett_util_storage_by_uuid_head_destroy (NMSettUtilStorageByUuidHead *sbuh); + +#define NM_SETT_UTIL_STORAGES_INIT(storages, storage_destroy_fcn) \ + { \ + .lst_head = C_LIST_INIT (((storages).lst_head)), \ + .idx_by_filename = g_hash_table_new_full (nm_str_hash, \ + g_str_equal, \ + NULL, \ + (GDestroyNotify) storage_destroy_fcn), \ + .idx_by_uuid = g_hash_table_new_full (nm_pstr_hash, \ + nm_pstr_equal, \ + NULL, \ + (GDestroyNotify) nm_sett_util_storage_by_uuid_head_destroy), \ + } + +void nm_sett_util_storages_clear (NMSettUtilStorages *storages); + +#define nm_auto_clear_sett_util_storages nm_auto(nm_sett_util_storages_clear) + +void nm_sett_util_storages_swap (NMSettUtilStorages *storages, + NMSettUtilStorages *other); + +void nm_sett_util_storages_add_take (NMSettUtilStorages *storages, + gpointer storage_take_p); + +gpointer nm_sett_util_storages_steal (NMSettUtilStorages *storages, + gpointer storage_p); + +gpointer nm_sett_util_storages_steal_and_replace (NMSettUtilStorages *storages, + gpointer storage_old_p, + gpointer storage_new_p); + +/*****************************************************************************/ + +static inline gpointer /* NMSettingsStorage * */ +nm_sett_util_storages_lookup_by_filename (NMSettUtilStorages *storages, + const char *filename) +{ + nm_assert (filename); + + return g_hash_table_lookup (storages->idx_by_filename, filename); +} + +static inline NMSettUtilStorageByUuidHead * +nm_sett_util_storages_lookup_by_uuid (NMSettUtilStorages *storages, + const char *uuid) +{ + nm_assert (uuid); + + return g_hash_table_lookup (storages->idx_by_uuid, &uuid); +} + +#endif /* __NM_SETTINGS_UTILS_H__ */ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 3086601fad..9622b0a13c 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -37,6 +37,7 @@ #include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-glib-aux/nm-keyfile-aux.h" +#include "nm-keyfile-internal.h" #include "nm-dbus-interface.h" #include "nm-connection.h" #include "nm-setting-8021x.h" @@ -169,7 +170,7 @@ _storage_data_is_alive (StorageData *sd) if (sd->connection) return TRUE; - if (nm_settings_storage_is_tombstone (sd->storage)) { + if (nm_settings_storage_is_keyfile_tombstone (sd->storage)) { /* entry does not have a profile, but it's here as a tombstone to * hide/shadow other connections. That's also relevant. */ return TRUE; @@ -781,7 +782,7 @@ _sett_conn_entry_sds_update (NMSettings *self, sd_dirty = _storage_data_find_in_lst (&sett_conn_entry->dirty_sd_lst_head, sd->storage); if (!sd_dirty) { - /* there is no update for this storage. */ + /* there is no update for this storage (except maybe reprioritize). */ if (reprioritize) sd->prioritize = FALSE; continue; @@ -921,7 +922,7 @@ _connection_changed_update (NMSettings *self, nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)); sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED; - if (nm_settings_storage_is_in_memory (storage)) + if (nm_settings_storage_is_keyfile_run (storage)) sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED; else { nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED)); @@ -1183,7 +1184,7 @@ _connection_changed_track (NMSettings *self, NM_SETTINGS_STORAGE_PRINT_ARG (storage), nm_connection_get_id (connection), NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", "")); - } else if (nm_settings_storage_is_tombstone (storage)) { + } else if (nm_settings_storage_is_keyfile_tombstone (storage)) { _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for hiding profile%s%s%s", sett_conn_entry->uuid, NM_SETTINGS_STORAGE_PRINT_ARG (storage), @@ -1250,6 +1251,9 @@ _plugin_connections_reload (NMSettings *self) TRUE, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS); + + for (iter = priv->plugins; iter; iter = iter->next) + nm_settings_plugin_load_connections_done (iter->data); } /*****************************************************************************/ @@ -1257,9 +1261,9 @@ _plugin_connections_reload (NMSettings *self) static gboolean _add_connection_to_first_plugin (NMSettings *self, NMConnection *new_connection, + gboolean in_memory, gboolean is_nm_generated, gboolean is_volatile, - gboolean in_memory, NMSettingsStorage **out_new_storage, NMConnection **out_new_connection, GError **error) @@ -1304,6 +1308,7 @@ _add_connection_to_first_plugin (NMSettings *self, &connection_to_add, &add_error); } + if (!success) { _LOGT ("add-connection: failed to add %s/'%s': %s", nm_connection_get_uuid (new_connection), @@ -1375,10 +1380,11 @@ nm_settings_add_connection (NMSettings *self, { gs_unref_object NMConnection *connection_cloned_1 = NULL; gs_unref_object NMConnection *new_connection = NULL; - gs_unref_object NMSettingsStorage *storage = NULL; + gs_unref_object NMSettingsStorage *new_storage = NULL; gs_free_error GError *local = NULL; SettConnEntry *sett_conn_entry; const char *uuid; + StorageData *sd; nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)); @@ -1422,12 +1428,12 @@ nm_settings_add_connection (NMSettings *self, if (!_add_connection_to_first_plugin (self, connection, - NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), - NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE), ( persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK || NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)), - &storage, + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE), + &new_storage, &new_connection, &local)) { g_set_error (error, @@ -1438,7 +1444,30 @@ nm_settings_add_connection (NMSettings *self, return FALSE; } - sett_conn_entry = _connection_changed_track (self, storage, new_connection, TRUE); + sett_conn_entry = _connection_changed_track (self, new_storage, new_connection, TRUE); + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + + if (!nm_settings_storage_is_keyfile_tombstone (sd->storage)) + continue; + + if (nm_settings_storage_is_keyfile_run (sd->storage)) { + /* We remove this file from /run. */ + } else { + if (nm_settings_storage_is_keyfile_run (new_storage)) { + /* Don't remove the file from /etc if we just wrote an in-memory connection */ + continue; + } + } + + nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (sd->storage), + sd->storage, + NULL); + + nm_assert (!nm_settings_storage_is_keyfile_tombstone (sd->storage)); + + _connection_changed_track (self, sd->storage, NULL, FALSE); + } _connection_changed_process_all_dirty (self, FALSE, @@ -1473,14 +1502,11 @@ nm_settings_update_connection (NMSettings *self, gs_unref_object NMConnection *new_connection_cloned = NULL; gs_unref_object NMConnection *new_connection = NULL; NMConnection *new_connection_real; - gs_free_error GError *local = NULL; gs_unref_object NMSettingsStorage *cur_storage = NULL; gs_unref_object NMSettingsStorage *new_storage = NULL; gboolean cur_in_memory; gboolean new_in_memory; - gboolean remove_from_disk; const char *uuid; - gboolean success; g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn), FALSE); @@ -1504,6 +1530,8 @@ nm_settings_update_connection (NMSettings *self, nm_assert (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid)) == sett_conn); if (connection) { + gs_free_error GError *local = NULL; + if (!_nm_connection_ensure_normalized (connection, FALSE, uuid, @@ -1526,7 +1554,7 @@ nm_settings_update_connection (NMSettings *self, } else connection = nm_settings_connection_get_connection (sett_conn); - cur_in_memory = !!nm_settings_storage_is_in_memory (cur_storage); + cur_in_memory = nm_settings_storage_is_keyfile_run (cur_storage); if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) { persist_mode = cur_in_memory @@ -1561,13 +1589,13 @@ nm_settings_update_connection (NMSettings *self, } if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST - && NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED - | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE) - && NM_FLAGS_ANY ((sett_flags ^ nm_settings_connection_get_flags (sett_conn)) & sett_flags, + && NM_FLAGS_ANY (sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE) + && NM_FLAGS_ANY ((sett_flags ^ nm_settings_connection_get_flags (sett_conn)) & sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) { /* we update the nm-generated/volatile setting of a profile (which is inherrently - * in-memory. The caller did not request to persis this to disk, however we need + * in-memory. The caller did not request to persist this to disk, however we need * to store the flags in run. */ nm_assert (cur_in_memory); persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED; @@ -1591,7 +1619,7 @@ nm_settings_update_connection (NMSettings *self, * For nm-generated profiles also, because the nm-generated flag is only stored * for in-memory profiles. If we would persist the profile to /etc it would loose * the nm-generated flag after restart/reload, and that cannot be right. If a profile - * ends up on disk, the information who created it get lost. */ + * ends up on disk, the information who created it gets lost. */ nm_assert (!NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)); sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED @@ -1600,74 +1628,91 @@ nm_settings_update_connection (NMSettings *self, | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE); } - remove_from_disk = (persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED); if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST) { - success = TRUE; new_storage = g_object_ref (cur_storage); new_connection = g_object_ref (connection); - } else if (new_in_memory != cur_in_memory) { - success = _add_connection_to_first_plugin (self, - connection, - NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), - NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), - new_in_memory, - &new_storage, - &new_connection, - &local); + _LOGT ("update[%s]: %s: update profile \"%s\" (not persisted)", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + nm_connection_get_id (connection)); } else { - NMSettingsPlugin *plugin; - - plugin = nm_settings_storage_get_plugin (cur_storage); - if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) { - success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin, - cur_storage, - connection, - NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), - NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), - NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME), - &new_storage, - &new_connection, - &local); + gboolean success; + gboolean migrate_storage; + gs_free_error GError *local = NULL; + + if (new_in_memory != cur_in_memory) + migrate_storage = TRUE; + else if ( !new_in_memory + && nm_settings_storage_is_keyfile_lib (cur_storage)) { + /* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it + * from /usr/lib to /etc. */ + migrate_storage = TRUE; + } else + migrate_storage = FALSE; + + if (migrate_storage) { + success = _add_connection_to_first_plugin (self, + connection, + new_in_memory, + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), + &new_storage, + &new_connection, + &local); } else { - success = nm_settings_plugin_update_connection (nm_settings_storage_get_plugin (cur_storage), - cur_storage, - connection, - &new_storage, - &new_connection, - &local); + NMSettingsPlugin *plugin; + + plugin = nm_settings_storage_get_plugin (cur_storage); + if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) { + success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin, + cur_storage, + connection, + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), + NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME), + &new_storage, + &new_connection, + &local); + } else { + success = nm_settings_plugin_update_connection (nm_settings_storage_get_plugin (cur_storage), + cur_storage, + connection, + &new_storage, + &new_connection, + &local); + } } - } - if (!success) { - gboolean ignore_failure; + if (!success) { + gboolean ignore_failure; - ignore_failure = NM_FLAGS_ANY (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE); + ignore_failure = NM_FLAGS_ANY (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE); - _LOGT ("update[%s]: %s: %sfailure to %s connection \"%s\" on storage: %s", - nm_settings_storage_get_uuid (cur_storage), - log_context_name, - ignore_failure ? "ignore " : "", - (new_in_memory != cur_in_memory) ? "write" : "update", - nm_connection_get_id (connection), - local->message); - if (!ignore_failure) { - g_set_error (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_CONNECTION, - "failed to %s connection: %s", - (new_in_memory != cur_in_memory) ? "write" : "update", - local->message); - return FALSE; + _LOGT ("update[%s]: %s: %sfailure to %s connection \"%s\" on storage: %s", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + ignore_failure ? "ignore " : "", + migrate_storage ? "write" : "update", + nm_connection_get_id (connection), + local->message); + if (!ignore_failure) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "failed to %s connection: %s", + migrate_storage ? "write" : "update", + local->message); + return FALSE; + } + new_storage = g_object_ref (cur_storage); + new_connection = g_object_ref (connection); + } else { + _LOGT ("update[%s]: %s: %s profile \"%s\"", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + migrate_storage ? "write" : "update", + nm_connection_get_id (connection)); } - g_clear_error (&local); - new_storage = g_object_ref (cur_storage); - new_connection = g_object_ref (connection); - } else { - _LOGT ("update[%s]: %s: update profile \"%s\"%s", - nm_settings_storage_get_uuid (cur_storage), - log_context_name, - nm_connection_get_id (connection), - persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST ? " (not persisted)" : ""); } nm_assert_valid_settings_storage (NULL, new_storage); @@ -1686,6 +1731,10 @@ nm_settings_update_connection (NMSettings *self, new_connection, agent_owned_secrets, &new_connection_cloned); + if (!new_connection_real) { + nm_assert_not_reached (); + new_connection_real = new_connection; + } } nm_assert (NM_IS_CONNECTION (new_connection_real)); @@ -1693,23 +1742,32 @@ nm_settings_update_connection (NMSettings *self, _connection_changed_track (self, new_storage, new_connection_real, TRUE); if (new_storage != cur_storage) { - if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage), - cur_storage, - remove_from_disk, - &local)) { - const char *filename; - - filename = nm_settings_storage_get_filename (cur_storage); - _LOGW ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s", - nm_settings_storage_get_uuid (cur_storage), - NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage), - local->message, - NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", "")); + gs_free_error GError *local = NULL; + gboolean remove_from_disk; + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED) + remove_from_disk = FALSE; + else if (nm_settings_storage_is_keyfile_lib (cur_storage)) + remove_from_disk = FALSE; + else + remove_from_disk = TRUE; + + if (remove_from_disk) { + if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage), + cur_storage, + &local)) { + const char *filename; + + filename = nm_settings_storage_get_filename (cur_storage); + _LOGW ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s", + nm_settings_storage_get_uuid (cur_storage), + NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage), + local->message, + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", "")); + } - g_clear_error (&local); + _connection_changed_track (self, cur_storage, NULL, FALSE); } - - _connection_changed_track (self, cur_storage, NULL, FALSE); } _connection_changed_process_all_dirty (self, @@ -1725,17 +1783,26 @@ nm_settings_update_connection (NMSettings *self, void nm_settings_delete_connection (NMSettings *self, NMSettingsConnection *sett_conn, - gboolean remove_from_disk, gboolean allow_add_to_no_auto_default) { + NMSettingsPrivate *priv; NMSettingsStorage *cur_storage; gs_free_error GError *local = NULL; SettConnEntry *sett_conn_entry = NULL; const char *uuid; + gboolean delete; + gboolean tombstone_in_memory = FALSE; + gboolean tombstone_on_disk = FALSE; + gs_unref_object NMSettingsStorage *tombstone_1_storage = NULL; + gs_unref_object NMSettingsStorage *tombstone_2_storage = NULL; g_return_if_fail (NM_IS_SETTINGS (self)); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn)); + nm_assert (nm_settings_has_connection (self, sett_conn)); + + priv = NM_SETTINGS_GET_PRIVATE (self); + cur_storage = nm_settings_connection_get_storage (sett_conn); nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage)); @@ -1749,19 +1816,66 @@ nm_settings_delete_connection (NMSettings *self, nm_assert (sett_conn_entry->sett_conn == sett_conn); nm_assert (sett_conn_entry->storage == cur_storage); - if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage), - cur_storage, - remove_from_disk, - &local)) { - _LOGW ("add-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s", - NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage), - local->message); - g_clear_error (&local); - /* there is no aborting back form this. We must get rid of the connection and - * cannot do better than warn. Proceed... */ + if (NMS_IS_KEYFILE_STORAGE (cur_storage)) { + NMSKeyfileStorage *s = NMS_KEYFILE_STORAGE (cur_storage); + + if (NM_IN_SET (s->storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) + delete = TRUE; + else { + tombstone_on_disk = TRUE; + delete = FALSE; + } + } else + delete = TRUE; + + if (delete) { + if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage), + cur_storage, + &local)) { + _LOGW ("delte-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s", + NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage), + local->message); + g_clear_error (&local); + /* there is no aborting back form this. We must get rid of the connection and + * cannot do better than warn. Proceed... */ + tombstone_in_memory = TRUE; + } + _connection_changed_track (self, cur_storage, NULL, FALSE); + } + + if (tombstone_on_disk) { + if (!nms_keyfile_plugin_update_loaded_uuid (priv->keyfile_plugin, + FALSE, + uuid, + FALSE, + TRUE, + &tombstone_1_storage, + NULL)) + tombstone_in_memory = TRUE; + if (tombstone_1_storage) + _connection_changed_track (self, tombstone_1_storage, NULL, FALSE); + } + + if (tombstone_in_memory) { + if (!nms_keyfile_plugin_update_loaded_uuid (priv->keyfile_plugin, + FALSE, + uuid, + TRUE, + TRUE, + &tombstone_2_storage, + NULL)) { + nms_keyfile_plugin_update_loaded_uuid (priv->keyfile_plugin, + TRUE, + uuid, + TRUE, + TRUE, + &tombstone_2_storage, + NULL); + } + _connection_changed_track (self, tombstone_2_storage, NULL, FALSE); } - _connection_changed_track (self, cur_storage, NULL, FALSE); _connection_changed_process_all_dirty (self, allow_add_to_no_auto_default, NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, @@ -2061,51 +2175,43 @@ impl_settings_load_connections (NMDBusObject *obj, NM_SETTINGS_ERROR_PERMISSION_DENIED)) return; - if (filenames) { + if ( filenames + && filenames[0]) { + NMSettingsPluginConnectionLoadEntry *entries; + gsize n_entries; gsize i; + GSList *iter; - for (i = 0; filenames[i]; i++) { - gboolean handled = FALSE; - - if (filenames[i][0] != '/') - _LOGW ("load: connection filename '%s' is not an absolute path", filenames[i]); - else { - GSList *iter; - - for (iter = priv->plugins; iter; iter = iter->next) { - NMSettingsPlugin *plugin = iter->data; - gs_unref_object NMSettingsStorage *storage = NULL; - gs_unref_object NMSettingsStorage *storage_replaced = NULL; - gs_unref_object NMConnection *connection_added = NULL; - - if (!nm_settings_plugin_load_connection (plugin, - filenames[i], - &storage, - &connection_added, - &storage_replaced, - NULL)) - continue; - - nm_assert ((!storage && !storage_replaced) || (storage != storage_replaced)); - - if (storage) { - nm_assert (connection_added); - _connection_changed_track (self, storage, connection_added, TRUE); - } - if (storage_replaced) - _connection_changed_track (self, storage_replaced, NULL, FALSE); - - handled = TRUE; - } - } + entries = nm_settings_plugin_create_connection_load_entries (filenames, &n_entries); - if (!handled) { - if (!failures) - failures = g_ptr_array_new (); - g_ptr_array_add (failures, (char *) filenames[i]); - } + for (iter = priv->plugins; iter; iter = iter->next) { + NMSettingsPlugin *plugin = iter->data; + + nm_settings_plugin_load_connections (plugin, + entries, + n_entries, + _plugin_connections_reload_cb, + self); + } + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *entry = &entries[i]; + + if (!entry->handled) + _LOGW ("load: no settings plugin could load \"%s\"", entry->filename); + else if (entry->error) { + _LOGW ("load: failure to load \"%s\": %s", entry->filename, entry->error->message); + g_clear_error (&entry->error); + } else + continue; + + if (!failures) + failures = g_ptr_array_new (); + g_ptr_array_add (failures, (char *) entry->filename); } + nm_clear_g_free (&entries); + _connection_changed_process_all_dirty (self, TRUE, NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, @@ -2113,6 +2219,9 @@ impl_settings_load_connections (NMDBusObject *obj, TRUE, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS); + + for (iter = priv->plugins; iter; iter = iter->next) + nm_settings_plugin_load_connections_done (iter->data); } if (failures) diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 737e917908..bcb30dff2a 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -116,7 +116,6 @@ gboolean nm_settings_update_connection (NMSettings *self, void nm_settings_delete_connection (NMSettings *self, NMSettingsConnection *sett_conn, - gboolean remove_from_disk, gboolean allow_add_to_no_auto_default); NMSettingsConnection *nm_settings_get_connection_by_path (NMSettings *settings, diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build index 5f8268e7a4..58acdcfcb1 100644 --- a/src/settings/plugins/ifcfg-rh/meson.build +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -35,7 +35,7 @@ libnms_ifcfg_rh_core = static_library( dependencies: deps, ) -sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-plugin.c') +sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-storage.c', 'nms-ifcfg-rh-plugin.c') libnm_settings_plugin_ifcfg_rh = shared_module( 'nm-settings-plugin-ifcfg-rh', diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index b260b3f114..cbbd497edb 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -36,8 +36,10 @@ #include "nm-core-internal.h" #include "nm-config.h" #include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-utils.h" #include "NetworkManagerUtils.h" +#include "nms-ifcfg-rh-storage.h" #include "nms-ifcfg-rh-common.h" #include "nms-ifcfg-rh-utils.h" #include "nms-ifcfg-rh-reader.h" @@ -52,16 +54,6 @@ struct _StorageData; -typedef struct { - char *filename; - char *uuid; - CList rd_lst; - NMConnection *connection; - char *unmanaged_spec; - char *unrecognized_spec; - struct timespec stat_mtime; -} ReadData; - typedef struct _StorageData { const char *filename; @@ -77,6 +69,11 @@ typedef struct _StorageData { } StorageData; typedef struct { + CList lst_head; + GHashTable *idx_by_filename; + GHashTable *idx_by_uuid; + + //XXX CList sd_lst_head; GHashTable *uuid_idx; GHashTable *filename_idx; @@ -94,6 +91,8 @@ typedef struct { Storages storages; + NMSettUtilStorages storagEs; + GHashTable *unmanaged_specs; GHashTable *unrecognized_specs; @@ -127,8 +126,8 @@ G_DEFINE_TYPE (NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN) static void _unhandled_specs_reset (NMSIfcfgRHPlugin *self); -static void _unhandled_specs_merge_read_data (NMSIfcfgRHPlugin *self, - CList *rd_lst_head); +static void _unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages); /*****************************************************************************/ @@ -162,36 +161,6 @@ _parse_unhandled_spec (const char *unhandled_spec, /*****************************************************************************/ -static void -_read_data_destroy (ReadData *rd) -{ - c_list_unlink_stale (&rd->rd_lst); - - g_free (rd->filename); - g_free (rd->uuid); - g_free (rd->unmanaged_spec); - g_free (rd->unrecognized_spec); - nm_g_object_unref (rd->connection); - - g_slice_free (ReadData, rd); -} - -static int -_read_data_sort_by_mtime (const CList *p_a, - const CList *p_b, - const void *user_data) -{ - ReadData *a = c_list_entry (p_a, ReadData, rd_lst); - ReadData *b = c_list_entry (p_b, ReadData, rd_lst); - - NM_CMP_FIELD (b, a, stat_mtime.tv_sec); - NM_CMP_FIELD (b, a, stat_mtime.tv_nsec); - NM_CMP_FIELD_STR (b, a, filename); - return 0; -} - -/*****************************************************************************/ - static StorageData * _storage_data_new (Storages *storages, const char *filename) @@ -215,16 +184,63 @@ _storage_data_new (Storages *storages, return sd; } +void +_nms_ifcfg_rh_storage_clear (NMSIfcfgRHStorage *self) +{ + c_list_unlink (&self->storage_lst); + c_list_unlink (&self->storage_by_uuid_lst); + g_free (self->unmanaged_spec); + g_free (self->unrecognized_spec); + nm_g_object_unref (self->connection); +} + +static void +_storage_destroy (NMSIfcfgRHStorage *storage) +{ + _nms_ifcfg_rh_storage_clear (storage); + g_object_unref (storage); +} + +static gboolean +_storage_equal_type (NMSIfcfgRHStorage *storage_a, + NMSIfcfgRHStorage *storage_b) +{ + return (storage_a == storage_b) + || ( storage_a + && storage_b + && nm_streq0 (nms_ifcfg_rh_storage_get_uuid_opt (storage_a), + nms_ifcfg_rh_storage_get_uuid_opt (storage_b)) + && nm_streq0 (storage_a->unmanaged_spec, + storage_b->unmanaged_spec) + && nm_streq0 (storage_a->unrecognized_spec, + storage_b->unrecognized_spec)); +} + static void -_storage_data_destroy (StorageData *sd) +_storage_copy_content (NMSIfcfgRHStorage *dst, + NMSIfcfgRHStorage *src) { - c_list_unlink_stale (&sd->sd_lst); - nm_g_object_unref (sd->storage); - g_free (sd->unmanaged_spec); - g_free (sd->unrecognized_spec); - nm_g_object_unref (sd->connection); + nm_assert (src != dst); + nm_assert (src && dst); + nm_assert (_storage_equal_type (dst, src)); + nm_assert ( nms_ifcfg_rh_storage_get_filename (dst) + && nm_streq (nms_ifcfg_rh_storage_get_filename (dst), + nms_ifcfg_rh_storage_get_filename (src))); + + nm_g_object_ref_set (&dst->connection, src->connection); + g_free (dst->unmanaged_spec); + g_free (dst->unrecognized_spec); + dst->unmanaged_spec = g_strdup (src->unmanaged_spec); + dst->unrecognized_spec = g_strdup (src->unrecognized_spec); + dst->stat_mtime = src->stat_mtime; +} + +static NMConnection * +_storage_steal_connection (NMSIfcfgRHStorage *storage) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); - g_free (sd); + return g_steal_pointer (&storage->connection); } static StorageData * @@ -241,18 +257,6 @@ _storages_by_uuid (Storages *storages, return g_hash_table_lookup (storages->uuid_idx, uuid); } -static void -_storages_init (Storages *storages) -{ - c_list_init (&storages->sd_lst_head); - storages->filename_idx = g_hash_table_new_full (nm_pstr_hash, - nm_pstr_equal, - (GDestroyNotify) _storage_data_destroy, - NULL); - storages->uuid_idx = g_hash_table_new (nm_str_hash, - g_str_equal); -} - /*****************************************************************************/ static void @@ -323,682 +327,422 @@ nm_assert_self (NMSIfcfgRHPlugin *self) /*****************************************************************************/ -static gboolean -load_connection_impl (NMSIfcfgRHPlugin *self, - const char *filename, - NMSettingsStorage **out_storage, - NMConnection **out_connection, - NMSettingsStorage **out_storage_replaced, - GError **error) +static NMSIfcfgRHStorage * +_load_file (NMSIfcfgRHPlugin *self, + const char *filename, + GError **error) { - NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); - gs_free char *ifcfg_path = NULL; gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *load_error = NULL; gs_free char *unhandled_spec = NULL; gs_free char *unmanaged_spec = NULL; gs_free char *unrecognized_spec = NULL; - gs_free_error GError *local = NULL; gboolean load_error_ignore; - StorageData *sd_f; - StorageData *sd_u; - const char *uuid; - gboolean connection_changed; - - if (nm_utils_file_is_in_path (filename, IFCFG_DIR)) { - /* get the real ifcfg-path. This allows us to properly - * handle load command using a route-* file etc. */ - ifcfg_path = utils_detect_ifcfg_path (filename, FALSE); + struct stat st; + + if (stat (filename, &st) != 0) { + int errsv = errno; + + if (error) { + nm_utils_error_set_errno (error, errsv, + "failure to stat file \%s\": %s", + filename); + } else + _LOGT ("load[%s]: failure to stat file: %s", filename, nm_strerror_native (errsv)); + return NULL; } - if (!ifcfg_path) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "path name does not name an ifcfg file"); - return FALSE; - } - - sd_f = _storages_by_filename (&priv->storages, ifcfg_path); - - connection = connection_from_file (ifcfg_path, + connection = connection_from_file (filename, &unhandled_spec, - &local, + &load_error, &load_error_ignore); - if (local) { - - if ( sd_f - && nm_utils_file_stat (ifcfg_path, NULL) == -ENOENT) { - - if (sd_f->connection) { - _LOGT ("load[%s]: %s (%s) deleted (file no longer present)", - sd_f->filename, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection)); - *out_storage_replaced = g_object_ref (sd_f->storage); - if (!g_hash_table_remove (priv->storages.uuid_idx, nm_settings_storage_get_uuid (sd_f->storage))) - nm_assert_not_reached (); - } else { - _LOGT ("load[%s]: %s spec \"%s\" deleted (file no longer present)", - sd_f->filename, - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); - } - if (!g_hash_table_remove (priv->storages.filename_idx, sd_f)) - nm_assert_not_reached (); - return TRUE; + if (load_error) { + if (error) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "failure to read file \"%s\": %s", + filename, load_error->message); + } else { + _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN, + "load[%s]: failure to read file: %s", filename, load_error->message); } - - _LOGT ("load[\"%s\"]: failed to load connection: %s", ifcfg_path, local->message); - g_propagate_error (error, g_steal_pointer (&local)); - return FALSE; + return NULL; } if (unhandled_spec) { g_clear_object (&connection); if (!_parse_unhandled_spec (unhandled_spec, &unmanaged_spec, - &unrecognized_spec)) - g_return_val_if_reached (FALSE); - } else - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - - uuid = connection ? nm_connection_get_uuid (connection) : NULL; - sd_u = connection ? _storages_by_uuid (&priv->storages, uuid) : NULL; - - /* There is a limitation of the load-API of NMSettingsPlugin. nm_settings_plugin_load_connection() - * can only load one file at at time. That means, on the first file the plugin might notice - * that a profile got moved/deleted, only to notice at the next file that the profile ss still - * present. - * - * Note that while NMSettings loads the file names one by one, it only keeps track - * of the result and processes them all at. That means, NMSettings corrects this - * limitation. - * - * The only problem here is our debug logging might indicate a removal, when we would later - * find out that it was a rename. But detecting complex renames/moves is anyway so complicated - * that it's not worth to fix this only for the purpose of a coherent logging output. - */ - - if (!sd_f) { - sd_f = _storage_data_new (&priv->storages, ifcfg_path); - - if (!connection) { - sd_f->unmanaged_spec = g_steal_pointer (&unmanaged_spec); - sd_f->unrecognized_spec = g_steal_pointer (&unrecognized_spec); - _LOGT ("load[%s]: %s spec \"%s\" added", - sd_f->filename, - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); - _unhandled_specs_reset (self); - return TRUE; - } - - if (!sd_u) { - sd_f->storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), - uuid, - sd_f->filename); - sd_f->connection = g_object_ref (connection); - _LOGT ("load[%s]: add %s (%s)", - sd_f->filename, - uuid, - nm_connection_get_id (connection)); - if (!g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f)) - nm_assert_not_reached (); - } else { - - sd_f->storage = g_object_ref (sd_u->storage); - connection_changed = !_connection_equals (connection, sd_u->connection); - if (!connection_changed) - sd_f->connection = g_object_ref (sd_u->connection); - else { - nmtst_connection_assert_unchanging (connection); - sd_f->connection = g_object_ref (connection); - } - - _LOGT ("load[%s]: update %s (%s) (%s%s \"%s\")", - sd_f->filename, - uuid, - nm_connection_get_id (sd_f->connection), - connection_changed ? "modified, " : "", - nm_utils_file_stat (sd_u->filename, NULL) == -ENOENT ? "renamed from" : "shadows previous file", - sd_u->filename); - - if (g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f)) - nm_assert_not_reached (); - - g_hash_table_remove (priv->storages.filename_idx, sd_u); - _nm_settings_storage_set_filename (sd_f->storage, sd_f->filename); + &unrecognized_spec)) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "invalid unhandled spec \"%s\"", + unhandled_spec); + nm_assert_not_reached (); + return NULL; } - - *out_storage = g_object_ref (sd_f->storage); - *out_connection = g_object_ref (sd_f->connection); - return TRUE; + return nms_ifcfg_rh_storage_new_unhandled (self, + filename, + g_steal_pointer (&unmanaged_spec), + g_steal_pointer (&unrecognized_spec), + &st.st_mtim); } - if (!connection) { + return nms_ifcfg_rh_storage_new_connection (self, + filename, + g_steal_pointer (&connection), + &st.st_mtim); +} - if ( nm_streq0 (unmanaged_spec, sd_f->unmanaged_spec) - && nm_streq0 (unrecognized_spec, sd_f->unrecognized_spec)) { - _LOGT ("load[%s]: %s spec \"%s\" unchanged", - sd_f->filename, - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); - return TRUE; - } +static void +_load_dir (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) +{ + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_free_error GError *local = NULL; + const char *f_filename; + GDir *dir; - if (!sd_f->connection) { - _LOGT ("load[%s]: %s spec \"%s\" changed (was %s spec \"%s\" before)", - sd_f->filename, - unmanaged_spec ? "unmanaged" : "unrecognized", - unmanaged_spec ?: unrecognized_spec, - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); - } else { - _LOGT ("load[%s]: %s spec \"%s\" changed (was %s (%s) before)", - sd_f->filename, - unmanaged_spec ? "unmanaged" : "unrecognized", - unmanaged_spec ?: unrecognized_spec, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection)); - } - g_free (sd_f->unmanaged_spec); - g_free (sd_f->unrecognized_spec); - sd_f->unmanaged_spec = g_steal_pointer (&unmanaged_spec); - sd_f->unrecognized_spec = g_steal_pointer (&unrecognized_spec); - g_clear_object (&sd_f->connection); - *out_storage_replaced = g_steal_pointer (&sd_f->storage); - return TRUE; + dir = g_dir_open (IFCFG_DIR, 0, &local); + if (!dir) { + _LOGT ("Could not read directory '%s': %s", IFCFG_DIR, local->message); + return; } - if (!sd_u) { - _LOGT ("load[%s]: add %s (%s) (was %s spec \"%s\")", - sd_f->filename, - uuid, - nm_connection_get_id (connection), - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); - nm_clear_g_free (&sd_f->unmanaged_spec); - nm_clear_g_free (&sd_f->unrecognized_spec); - sd_f->connection = g_steal_pointer (&connection); - sd_f->storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, sd_f->filename); + while ((f_filename = g_dir_read_name (dir))) { + gs_free char *full_path = NULL; + NMSIfcfgRHStorage *storage; + char *full_filename; - if (!g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f)) - nm_assert_not_reached (); - - *out_storage = g_object_ref (sd_f->storage); - *out_connection = g_object_ref (sd_f->connection); - return TRUE; - } - - if (sd_f == sd_u) { + full_path = g_build_filename (IFCFG_DIR, f_filename, NULL); + full_filename = utils_detect_ifcfg_path (full_path, TRUE); + if (!full_filename) + continue; - connection_changed = !_connection_equals (connection, sd_f->connection); + if (!g_hash_table_add (dupl_filenames, full_filename)) + continue; - if (!connection_changed) { - _LOGT ("load[%s]: unchanged %s (%s)", - sd_f->filename, - uuid, - nm_connection_get_id (sd_f->connection)); - } else { - _LOGT ("load[%s]: update %s (%s) (modified)", - sd_f->filename, - uuid, - nm_connection_get_id (connection)); - nm_g_object_ref_set (&sd_f->connection, connection); - } + nm_assert (!nm_sett_util_storages_lookup_by_filename (storages, full_filename)); - *out_storage = g_object_ref (sd_f->storage); - *out_connection = g_object_ref (sd_f->connection); - return TRUE; + storage = _load_file (self, + full_filename, + NULL); + if (storage) + nm_sett_util_storages_add_take (storages, storage); } - - connection_changed = !_connection_equals (connection, sd_u->connection); - - _LOGT ("load[%s]: update %s (%s) (%sfile %s \"%s\" replacing %s (%s))", - sd_f->filename, - uuid, - nm_connection_get_id (connection), - connection_changed ? "modified, " : "", - nm_utils_file_stat (sd_u->filename, NULL) == -ENOENT ? "renamed from" : "shadows previous file", - sd_u->filename, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection)); - - if (connection_changed) - nm_g_object_ref_set (&sd_f->connection, sd_u->connection); - else - nm_g_object_ref_set (&sd_f->connection, connection); - - *out_storage_replaced = g_steal_pointer (&sd_f->storage); - - sd_f->storage = g_object_ref (sd_u->storage); - - _nm_settings_storage_set_filename (sd_f->storage, sd_f->filename); - - if (g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f)) - nm_assert_not_reached (); - - g_hash_table_remove (priv->storages.filename_idx, sd_u); - - *out_storage = g_object_ref (sd_f->storage); - *out_connection = g_object_ref (sd_f->connection); - return TRUE; -} - -static gboolean -load_connection (NMSettingsPlugin *plugin, - const char *filename, - NMSettingsStorage **out_storage, - NMConnection **out_connection, - NMSettingsStorage **out_storage_replaced, - GError **error) -{ - NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); - gboolean success; - - nm_assert_self (self); - success = load_connection_impl (self, filename, out_storage, out_connection, out_storage_replaced, error); - nm_assert_self (self); - return success; + g_dir_close (dir); } static void -reload_connections (NMSettingsPlugin *plugin, - NMSettingsPluginConnectionReloadCallback callback, - gpointer user_data) +_storages_consolidate (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); - gs_unref_hashtable GHashTable *rd_idx_by_uuid = NULL; - gs_unref_hashtable GHashTable *rd_idx_by_filename = NULL; - gs_free_error GError *local = NULL; - CList rd_lst_head; - StorageData *sd_safe; - StorageData *sd; - StorageData *sd2; - ReadData *rd_safe; - ReadData *rd; - GDir *dir; - gs_unref_ptrarray GPtrArray *storages_changed = NULL; - Storages new_storages = { }; - - nm_assert_self (self); - - storages_changed = g_ptr_array_new_with_free_func (g_object_unref); - - c_list_init (&rd_lst_head); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSIfcfgRHStorage *storage_safe; + NMSIfcfgRHStorage *storage; + guint i; - rd_idx_by_uuid = g_hash_table_new (nm_str_hash, g_str_equal); - rd_idx_by_filename = g_hash_table_new (nm_pstr_hash, nm_pstr_equal); + /* when we reload all files, we must signal add/update/modify of profiles one-by-one. + * NMSettings then goes ahead and emits further signals and a lot of things happen. + * + * So, first, emit an update of the unmanaged/unrecognized specs that contains *all* + * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized + * specs have the meaning of "not doing something", it makes sense that we temporarily + * disable that action for the sum of before and after. */ + _unhandled_specs_merge_storages (self, storages_new); + + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); + + c_list_for_each_entry (storage, &storages_new->lst_head, storage_lst) + storage->dirty = TRUE; + + c_list_for_each_entry_safe (storage, storage_safe, &priv->storagEs.lst_head, storage_lst) { + gs_unref_object NMSIfcfgRHStorage *storage_2 = NULL; + gs_unref_object NMSIfcfgRHStorage *storage_loaded_2 = NULL; + NMSIfcfgRHStorage *storage_loaded; + + nm_assert (!storage->connection); + + storage_loaded = nm_sett_util_storages_lookup_by_filename (storages_new, nms_ifcfg_rh_storage_get_filename (storage)); + + if ( !storage_loaded + || !_storage_equal_type (storage, storage_loaded)) { + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage))) { + storage_2 = nm_sett_util_storages_steal (&priv->storagEs, storage); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_2)) { + c_list_link_tail (&storages_deleted, &storage_2->storage_lst); + g_steal_pointer (&storage_2); + } else + _storage_destroy (storage_2); + } + continue; + } - /* load the list of filenames... */ + storage_2 = nm_sett_util_storages_steal (&priv->storagEs, storage); - dir = g_dir_open (IFCFG_DIR, 0, &local); - if (!dir) { - _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, local->message); - g_clear_error (&local); - } else { - const char *item; - - while ((item = g_dir_read_name (dir))) { - gs_free char *full_path = NULL; - gs_free char *real_path = NULL; - struct stat st; - - full_path = g_build_filename (IFCFG_DIR, item, NULL); - real_path = utils_detect_ifcfg_path (full_path, TRUE); - if (!real_path) - continue; + _storage_copy_content (storage_2, storage_loaded); - rd = g_hash_table_lookup (rd_idx_by_filename, &real_path); - if (rd) - continue; + storage_2->dirty = FALSE; - if (stat (real_path, &st) != 0) { - int errsv = errno; + storage_loaded_2 = nm_sett_util_storages_steal_and_replace (storages_new, storage_loaded, storage_2); - _LOGT ("load[%s]: failure to stat file: %s", real_path, nm_strerror_native (errsv)); - continue; - } + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_2)) + g_ptr_array_add (storages_modified, g_steal_pointer (&storage_2)); + } - rd = g_slice_new (ReadData); - *rd = (ReadData) { - .filename = g_steal_pointer (&real_path), - .stat_mtime = st.st_mtim, - }; - c_list_link_tail (&rd_lst_head, &rd->rd_lst); - g_hash_table_add (rd_idx_by_filename, rd); - } - g_dir_close (dir); + c_list_for_each_entry (storage, &storages_new->lst_head, storage_lst) { + if (storage->dirty) { + if (nms_ifcfg_rh_storage_get_uuid_opt (storage)) + g_ptr_array_add (storages_modified, g_object_ref (storage)); + } else + storage->dirty = TRUE; } - /* load the connections/unhandled-spec from the filenames... */ + nm_sett_util_storages_swap (&priv->storagEs, storages_new); - c_list_sort (&rd_lst_head, _read_data_sort_by_mtime, NULL); + /* raise events. */ - c_list_for_each_entry_safe (rd, rd_safe, &rd_lst_head, rd_lst) { - gs_free_error GError *load_error = NULL; - gs_free char *unhandled_spec = NULL; - gboolean load_error_ignore; - const char *uuid; - ReadData *rd_dup; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; + storage = storages_modified->pdata[i]; - rd->connection = connection_from_file (rd->filename, - &unhandled_spec, - &load_error, - &load_error_ignore); - if (load_error) { - _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN, - "load[%s]: failure to read file: %s", rd->filename, load_error->message); - goto destroy_rd_1; + if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storagEs, nms_ifcfg_rh_storage_get_filename (storage))) { + /* hm? The profile was deleted in the meantime? That is only possible + * if the signal handler called again into the plugin. In any case, the event + * was already emitted. Skip. */ + continue; } - - if (unhandled_spec) { - g_clear_object (&rd->connection); - if (!_parse_unhandled_spec (unhandled_spec, - &rd->unmanaged_spec, - &rd->unrecognized_spec)) - goto destroy_rd_1; + if (!storage->dirty) { + /* the entry is no longer dirty. In the meantime we already emited + * another signal for it. */ continue; } - nm_assert (NM_IS_CONNECTION (rd->connection)); - nm_assert (_nm_connection_verify (rd->connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + storage->dirty = FALSE; - nmtst_connection_assert_unchanging (rd->connection); + connection = _storage_steal_connection (storage); - uuid = nm_connection_get_uuid (rd->connection); - nm_assert (nm_utils_is_uuid (uuid)); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); + } - rd_dup = g_hash_table_lookup (rd_idx_by_uuid, uuid); - if (rd_dup) { - _LOGW ("load[%s]: file (uuid %s) shadowed by file \"%s\"", - uuid, - rd->filename, - rd_dup->filename); - goto destroy_rd_1; - } + while ((storage = c_list_first_entry (&storages_deleted, NMSIfcfgRHStorage, storage_lst))) { + c_list_unlink (&storage->storage_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + _storage_destroy (storage); + } +} - rd->uuid = g_strdup (uuid); - g_hash_table_insert (rd_idx_by_uuid, rd->uuid, rd); - continue; +/*****************************************************************************/ -destroy_rd_1: - g_hash_table_remove (rd_idx_by_filename, rd); - _read_data_destroy (rd); - } +static void +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, _storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - /* when we reload all files, we must signal add/update/modify of profiles one-by-one. - * NMSettings then goes ahead and emits further signals and a lot of things happen. - * - * So, first, emit an update of the unmanaged/unrecognized specs that contains *all* - * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized - * specs have the meaning of "not doing something", it makes sense that we temporarily - * disable that action for the sum of before and after. */ - _unhandled_specs_merge_read_data (self, &rd_lst_head); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); - _storages_init (&new_storages); + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + gs_free_error GError *local = NULL; + const char *full_filename; + const char *uuid; + gs_free char *full_filename_keep = NULL; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gs_unref_object NMSIfcfgRHStorage *storage = NULL; - /* first find and remove the storage-data that is gone entirely. */ + if (entry->handled) + continue; - c_list_for_each_entry_safe (sd, sd_safe, &priv->storages.sd_lst_head, sd_lst) { + if (entry->filename[0] != '/') + continue; - rd = g_hash_table_lookup (rd_idx_by_filename, &sd->filename); + full_filename_keep = utils_detect_ifcfg_path (entry->filename, FALSE); - if (!sd->connection) { - if (!rd) { - _LOGT ("load[%s]: %s spec \"%s\" deleted (file no longer present)", - sd->filename, - sd->unmanaged_spec ? "unmanaged" : "unrecognized", - sd->unmanaged_spec ?: sd->unrecognized_spec); - g_hash_table_remove (priv->storages.filename_idx, sd); + if (!full_filename_keep) { + if (nm_utils_file_is_in_path (entry->filename, IFCFG_DIR)) { + nm_utils_error_set (&entry->error, + NM_UTILS_ERROR_UNKNOWN, + ("path is not a valid name for an ifcfg-rh file")); + entry->handled = TRUE; } continue; } - if ( !rd - && !g_hash_table_lookup (rd_idx_by_uuid, nm_settings_storage_get_uuid (sd->storage))) { - _LOGT ("load[%s]: %s (%s) deleted (file no longer present)", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection)); - g_ptr_array_add (storages_changed, g_object_ref (sd->storage)); - g_hash_table_remove (rd_idx_by_uuid, nm_settings_storage_get_uuid (sd->storage)); - g_hash_table_remove (priv->storages.uuid_idx, nm_settings_storage_get_uuid (sd->storage)); - g_hash_table_remove (priv->storages.filename_idx, sd); + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; } - } - - c_list_for_each_entry (rd, &rd_lst_head, rd_lst) { - sd = _storage_data_new (&new_storages, rd->filename); + entry->handled = TRUE; - sd->unmanaged_spec = g_strdup (rd->unmanaged_spec); - sd->unrecognized_spec = g_strdup (rd->unrecognized_spec); - sd->connection = g_object_ref (rd->connection); + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); - if (!rd->connection) + storage = _load_file (self, + full_filename, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSIfcfgRHStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storagEs, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; + } + g_propagate_error (&entry->error, g_steal_pointer (&local)); continue; - - sd2 = _storages_by_uuid (&priv->storages, rd->uuid); - if (sd2) - sd->storage = g_object_ref (sd2->storage); - else { - sd->storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (plugin), - rd->uuid, - rd->filename); } - if (!g_hash_table_replace (new_storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd->storage), sd)) - nm_assert_not_reached (); - } + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + if (uuid) + g_hash_table_add (loaded_uuids, (char *) uuid); - if (_LOGT_ENABLED ()) { - c_list_for_each_entry (sd, &new_storages.sd_lst_head, sd_lst) { - StorageData *sd_f; - StorageData *sd_u; - gboolean connection_changed; - - sd_f = _storages_by_filename (&priv->storages, sd->filename); - - if (!sd->connection) { - - if (!sd_f) { - _LOGT ("load[%s]: %s spec \"%s\" added", - sd->filename, - sd->unmanaged_spec ? "unmanaged" : "unrecognized", - sd->unmanaged_spec ?: sd->unrecognized_spec); - continue; - } - - if ( nm_streq0 (sd->unmanaged_spec, sd_f->unmanaged_spec) - && nm_streq0 (sd->unrecognized_spec, sd_f->unrecognized_spec)) { - _LOGT ("load[%s]: %s spec \"%s\" unchanged", - sd->filename, - sd->unmanaged_spec ? "unmanaged" : "unrecognized", - sd->unmanaged_spec ?: sd->unrecognized_spec); - continue; - } - - if (!sd_f->connection) { - _LOGT ("load[%s]: %s spec \"%s\" changed (was %s spec \"%s\" before)", - sd->filename, - sd->unmanaged_spec ? "unmanaged" : "unrecognized", - sd->unmanaged_spec ?: sd->unrecognized_spec, - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); - continue; - } - - sd2 = _storages_by_uuid (&new_storages, nm_settings_storage_get_uuid (sd_f->storage)); - - if (!sd2) { - _LOGT ("load[%s]: %s spec \"%s\" changed (was %s (%s) before)", - sd->filename, - sd->unmanaged_spec ? "unmanaged" : "unrecognized", - sd->unmanaged_spec ?: sd->unrecognized_spec, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection)); - continue; - } - - _LOGT ("load[%s]: %s spec \"%s\" changed (was %s (%s) before which got renamed to \"%s\")", - sd->filename, - sd->unmanaged_spec ? "unmanaged" : "unrecognized", - sd->unmanaged_spec ?: sd->unrecognized_spec, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection), - sd2->filename); - continue; + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); + } - } + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSIfcfgRHStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; - sd_u = _storages_by_uuid (&priv->storages, nm_settings_storage_get_uuid (sd->storage)); - - if (!sd_u) { - if (!sd_f) { - _LOGT ("load[%s]: add %s (%s)", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection)); - } else { - nm_assert (!sd_f->connection); - _LOGT ("load[%s]: add %s (%s) (was %s spec \"%s\")", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection), - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); - } - continue; - } - - connection_changed = !_connection_equals (sd->connection, sd_u->connection); - - if (sd_u == sd_f) { - if (!connection_changed) { - _LOGT ("load[%s]: unchanged %s (%s)", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection)); - continue; - } - _LOGT ("load[%s]: update %s (%s) (modified)", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection)); - continue; - } + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storagEs, loaded_uuid); + if (!sbuh) + continue; - if (!sd_f) { - _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\")", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection), - connection_changed ? "modified, " : "", - sd_u->filename); - continue; - } + c_list_for_each_entry (storage, &sbuh->lst_by_uuid_head, storage_by_uuid_lst) { + const char *full_filename = nms_ifcfg_rh_storage_get_filename (storage); + gs_unref_object NMSIfcfgRHStorage *storage_new = NULL; + gs_free_error GError *local = NULL; - if (!sd_f->connection) { - _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\" replacing %s spec \"%s\")", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection), - connection_changed ? "modified, " : "", - sd_u->filename, - sd_f->unmanaged_spec ? "unmanaged" : "unrecognized", - sd_f->unmanaged_spec ?: sd_f->unrecognized_spec); + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ continue; } - rd = g_hash_table_lookup (rd_idx_by_uuid, nm_settings_storage_get_uuid (sd_f->storage)); - - if (!rd) { - _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\" replacing %s (%s))", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection), - connection_changed ? "modified, " : "", - sd_u->filename, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection)); - continue; + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file (self, + full_filename, + &local); + if ( storage_new + && !nm_streq0 (loaded_uuid, nms_ifcfg_rh_storage_get_uuid_opt (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); } - _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\" and renaming previous %s (%s) to \"%s\")", - sd->filename, - nm_settings_storage_get_uuid (sd->storage), - nm_connection_get_id (sd->connection), - connection_changed ? "modified, " : "", - sd_u->filename, - nm_settings_storage_get_uuid (sd_f->storage), - nm_connection_get_id (sd_f->connection), - rd->filename); + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); } } + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); - c_list_for_each_entry (sd, &new_storages.sd_lst_head, sd_lst) { - if (sd->connection) { - _nm_settings_storage_set_filename (sd->storage, sd->filename); - g_ptr_array_add (storages_changed, g_object_ref (sd->storage)); - } - } - - - nm_clear_pointer (&rd_idx_by_uuid, g_hash_table_destroy); - nm_clear_pointer (&rd_idx_by_filename, g_hash_table_destroy); - - while ((rd = c_list_first_entry (&rd_lst_head, ReadData, rd_lst))) - _read_data_destroy (rd); - - - nm_clear_pointer (&priv->storages.uuid_idx, g_hash_table_destroy); - nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy); - - nm_assert (c_list_is_empty (&priv->storages.sd_lst_head)); - - priv->storages.uuid_idx = new_storages.uuid_idx; - priv->storages.filename_idx= new_storages.filename_idx; - c_list_swap (&priv->storages.sd_lst_head, &new_storages.sd_lst_head); - - - if (storages_changed->len > 0) { - gs_unref_hashtable GHashTable *h_unique = NULL; - guint i; - - h_unique = g_hash_table_new (nm_direct_hash, NULL); - - for (i = 0; i < storages_changed->len; i++) { - NMSettingsStorage *storage = storages_changed->pdata[i]; + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); +} - if (!g_hash_table_add (h_unique, storage)) - continue; +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, _storage_destroy); - sd = _storages_by_uuid (&priv->storages, nm_settings_storage_get_uuid (storage)); - callback (NM_SETTINGS_PLUGIN (self), - storage, - sd && sd->connection ? sd->connection : NULL, - user_data); - } - } + nm_assert_self (self); + _load_dir (self, &storages_new); - _unhandled_specs_reset (self); + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); nm_assert_self (self); } +static void +load_connections_done (NMSettingsPlugin *plugin) +{ + /* at the beginning of a load, we emit a change signal for unmanaged/unrecognized + * specs that contain the sum of before and after (_unhandled_specs_merge_storages()). + * + * The idea is that while we emit signals about changes to connection, we have + * the sum of all unmanaged/unrecognized devices from before and after. + * + * This if triggered at the end, to reset the specs. */ + _unhandled_specs_reset (NMS_IFCFG_RH_PLUGIN (plugin)); +} + +/*****************************************************************************/ + typedef struct { - GHashTable *filename_idx; + GHashTable *idx_by_filename; const char *allowed_filename; } AllowFilenameData; #define ALLOW_FILENAME_DATA(_filename_idx, _allowed_filename) \ (&((AllowFilenameData) { \ - .filename_idx = (_filename_idx), \ + .idx_by_filename = (_filename_idx), \ .allowed_filename = (_allowed_filename), \ })) @@ -1008,12 +752,11 @@ _allow_filename_cb (const char *filename, { const AllowFilenameData *allow_filename_data = user_data; - if (!g_hash_table_contains (allow_filename_data->filename_idx, &filename)) - return TRUE; if ( allow_filename_data->allowed_filename && nm_streq (allow_filename_data->allowed_filename, filename)) return TRUE; - return FALSE; + + return !g_hash_table_contains (allow_filename_data->idx_by_filename, filename); } static gboolean @@ -1039,21 +782,11 @@ add_connection (NMSettingsPlugin *plugin, uuid = nm_connection_get_uuid (connection); - if (_storages_by_uuid (&priv->storages, uuid)) { - _LOGT ("add: %s, \"%s\": failed to add connection that already exists", - uuid, - nm_connection_get_id (connection)); - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "connection with UUID %s already exists", - uuid); - return FALSE; - } - if (!nms_ifcfg_rh_writer_write_connection (connection, IFCFG_DIR, NULL, _allow_filename_cb, - ALLOW_FILENAME_DATA (priv->storages.filename_idx, NULL), + ALLOW_FILENAME_DATA (priv->storagEs.idx_by_filename, NULL), &full_filename, &reread, &reread_same, @@ -1178,7 +911,6 @@ update_connection (NMSettingsPlugin *plugin, static gboolean delete_connection (NMSettingsPlugin *plugin, NMSettingsStorage *storage, - gboolean remove_from_disk, GError **error) { NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); @@ -1188,6 +920,9 @@ delete_connection (NMSettingsPlugin *plugin, const char *filename; const char *uuid; + //XXX + gboolean remove_from_disk = FALSE; + nm_assert_self (self); nm_assert (!error || !*error); @@ -1261,16 +996,16 @@ _unhandled_specs_reset (NMSIfcfgRHPlugin *self) NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); gs_unref_hashtable GHashTable *unmanaged_specs = NULL; gs_unref_hashtable GHashTable *unrecognized_specs = NULL; - StorageData *sd; + NMSIfcfgRHStorage *storage; unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - c_list_for_each_entry (sd, &priv->storages.sd_lst_head, sd_lst) { - if (sd->unmanaged_spec) - g_hash_table_add (unmanaged_specs, g_strdup (sd->unmanaged_spec)); - if (sd->unrecognized_spec) - g_hash_table_add (unrecognized_specs, g_strdup (sd->unrecognized_spec)); + c_list_for_each_entry (storage, &priv->storagEs.lst_head, storage_lst) { + if (storage->unmanaged_spec) + g_hash_table_add (unmanaged_specs, g_strdup (storage->unmanaged_spec)); + if (storage->unrecognized_spec) + g_hash_table_add (unrecognized_specs, g_strdup (storage->unrecognized_spec)); } if (!nm_utils_hashtable_same_keys (unmanaged_specs, priv->unmanaged_specs)) { @@ -1289,24 +1024,24 @@ _unhandled_specs_reset (NMSIfcfgRHPlugin *self) } static void -_unhandled_specs_merge_read_data (NMSIfcfgRHPlugin *self, - CList *rd_lst_head) +_unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); gboolean unmanaged_changed = FALSE; gboolean unrecognized_changed = FALSE; - ReadData *rd; + NMSIfcfgRHStorage *storage; - c_list_for_each_entry (rd, rd_lst_head, rd_lst) { - if ( rd->unmanaged_spec - && !g_hash_table_contains (priv->unmanaged_specs, rd->unmanaged_spec)) { + c_list_for_each_entry (storage, &storages->lst_head, storage_lst) { + if ( storage->unmanaged_spec + && !g_hash_table_contains (priv->unmanaged_specs, storage->unmanaged_spec)) { unmanaged_changed = TRUE; - g_hash_table_add (priv->unmanaged_specs, g_strdup (rd->unmanaged_spec)); + g_hash_table_add (priv->unmanaged_specs, g_strdup (storage->unmanaged_spec)); } - if ( rd->unrecognized_spec - && !g_hash_table_contains (priv->unrecognized_specs, rd->unrecognized_spec)) { + if ( storage->unrecognized_spec + && !g_hash_table_contains (priv->unrecognized_specs, storage->unrecognized_spec)) { unrecognized_changed = TRUE; - g_hash_table_add (priv->unrecognized_specs, g_strdup (rd->unrecognized_spec)); + g_hash_table_add (priv->unrecognized_specs, g_strdup (storage->unrecognized_spec)); } } @@ -1652,7 +1387,7 @@ nms_ifcfg_rh_plugin_init (NMSIfcfgRHPlugin *self) priv->unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); priv->unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - _storages_init (&priv->storages); + priv->storagEs = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, _storage_destroy); } static void @@ -1684,8 +1419,7 @@ dispose (GObject *object) * when NMSettings is shutting down, and not when the instance gets destroyed. */ _dbus_clear (self); - nm_clear_pointer (&priv->storages.uuid_idx, g_hash_table_destroy); - nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy); + nm_sett_util_storages_clear (&priv->storagEs); g_clear_object (&priv->config); @@ -1707,8 +1441,9 @@ nms_ifcfg_rh_plugin_class_init (NMSIfcfgRHPluginClass *klass) plugin_class->plugin_name = "ifcfg-rh"; plugin_class->get_unmanaged_specs = get_unmanaged_specs; plugin_class->get_unrecognized_specs = get_unrecognized_specs; - plugin_class->load_connection = load_connection; plugin_class->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; + plugin_class->load_connections_done = load_connections_done; plugin_class->add_connection = add_connection; plugin_class->update_connection = update_connection; plugin_class->delete_connection = delete_connection; diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c new file mode 100644 index 0000000000..4fbc202529 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c @@ -0,0 +1,122 @@ +/* NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nms-ifcfg-rh-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-connection.h" +#include "nms-ifcfg-rh-plugin.h" + +/*****************************************************************************/ + +struct _NMSIfcfgRHStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSIfcfgRHStorage, nms_ifcfg_rh_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +static void +nms_ifcfg_rh_storage_init (NMSIfcfgRHStorage *self) +{ + c_list_init (&self->storage_lst); + c_list_init (&self->storage_by_uuid_lst); +} + +static NMSIfcfgRHStorage * +_storage_new (NMSIfcfgRHPlugin *plugin, + const char *uuid, + const char *filename) +{ + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (plugin)); + nm_assert (!uuid || nm_utils_is_uuid (uuid)); + + return g_object_new (NMS_TYPE_IFCFG_RH_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_connection (NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime) +{ + NMSIfcfgRHStorage *self; + + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, + nm_connection_get_uuid (connection_take), + filename); + self->connection = connection_take; + if (mtime) + self->stat_mtime = *mtime; + return self; +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_unhandled (NMSIfcfgRHPlugin *plugin, + const char *filename, + char *unmanaged_spec_take, + char *unrecognized_spec_take, + const struct timespec *mtime) +{ + NMSIfcfgRHStorage *self; + + nm_assert (unmanaged_spec_take || unrecognized_spec_take); + + self = _storage_new (plugin, + NULL, + filename); + self->unmanaged_spec = unmanaged_spec_take; + self->unrecognized_spec = unrecognized_spec_take; + if (mtime) + self->stat_mtime = *mtime; + return self; +} + +static void +dispose (GObject *object) +{ + NMSIfcfgRHStorage *self = NMS_IFCFG_RH_STORAGE (object); + + _nms_ifcfg_rh_storage_clear (self); + + G_OBJECT_CLASS (nms_ifcfg_rh_storage_parent_class)->dispose (object); +} + +static void +nms_ifcfg_rh_storage_class_init (NMSIfcfgRHStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) _nms_ifcfg_rh_storage_cmp_fcn; +} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h new file mode 100644 index 0000000000..c584dc3593 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h @@ -0,0 +1,91 @@ +/* NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2019 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_STORAGE_H__ +#define __NMS_IFCFG_RH_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" + +/*****************************************************************************/ + +#define NMS_TYPE_IFCFG_RH_STORAGE (nms_ifcfg_rh_storage_get_type ()) +#define NMS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) +#define NMS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) +#define NMS_IS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IFCFG_RH_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) + +typedef struct { + NMSettingsStorage parent; + + CList storage_lst; + + CList storage_by_uuid_lst; + + NMConnection *connection; + + char *unmanaged_spec; + char *unrecognized_spec; + + /* The timestamp (stat's mtime) of the file. Newer files have + * higher priority. */ + struct timespec stat_mtime; + + bool dirty:1; + +} NMSIfcfgRHStorage; + +typedef struct _NMSIfcfgRHStorageClass NMSIfcfgRHStorageClass; + +GType nms_ifcfg_rh_storage_get_type (void); + +struct _NMSIfcfgRHPlugin; + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_connection (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime); + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_unhandled (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + char *unmanaged_spec_take, + char *unrecognized_spec_take, + const struct timespec *mtime); + +void _nms_ifcfg_rh_storage_clear (NMSIfcfgRHStorage *storage); + +int _nms_ifcfg_rh_storage_cmp_fcn (NMSIfcfgRHStorage *a, + NMSIfcfgRHStorage *b); + +/*****************************************************************************/ + +static inline const char * +nms_ifcfg_rh_storage_get_uuid_opt (NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_uuid_opt ((NMSettingsStorage *) self); +} + +static inline const char * +nms_ifcfg_rh_storage_get_filename (NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_filename ((NMSettingsStorage *) self); +} + +#endif /* __NMS_IFCFG_RH_STORAGE_H__ */ diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index f8941021e5..3f9f4d6983 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -95,7 +95,7 @@ _storage_data_destroy (StorageData *sd) static void reload_connections (NMSettingsPlugin *plugin, - NMSettingsPluginConnectionReloadCallback callback, + NMSettingsPluginConnectionLoadCallback callback, gpointer user_data) { NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index 3b9435440b..dc4d711147 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -25,7 +25,7 @@ #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> -#include <glib/gstdio.h> +#include <sys/time.h> #include "nm-std-aux/c-list-util.h" #include "nm-glib-aux/nm-c-list.h" @@ -43,6 +43,7 @@ #include "settings/nm-settings-plugin.h" #include "settings/nm-settings-storage.h" +#include "settings/nm-settings-utils.h" #include "nms-keyfile-storage.h" #include "nms-keyfile-writer.h" @@ -52,37 +53,6 @@ /*****************************************************************************/ typedef struct { - CList crld_lst; - - char *full_filename; - const char *filename; - - /* the profile loaded from the file. Note that this profile is only relevant - * during _do_reload_all(). The winning profile at the end of reload will - * be referenced as connection_exported, the connection field here will be - * cleared. */ - NMConnection *connection; - - /* the following fields are only required during _do_reload_all() for comparing - * which profile is the most relevant one (in case multple files provide a profile - * with the same UUID). */ - struct timespec stat_mtime; - dev_t stat_dev; - ino_t stat_ino; - NMSKeyfileStorageType storage_type:3; - guint storage_priority:13; - NMTernary is_nm_generated_opt:3; - NMTernary is_volatile_opt:3; -} ConnReloadData; - -typedef struct _NMSKeyfileConnReloadHead { - CList crld_lst_head; - - char *loaded_path_etc; - char *loaded_path_run; -} ConnReloadHead; - -typedef struct { NMConfig *config; @@ -103,11 +73,7 @@ typedef struct { char *dirname_etc; char *dirname_run; - struct { - CList lst_head; - GHashTable *idx; - GHashTable *filename_idx; - } storages; + NMSettUtilStorages storages; } NMSKeyfilePluginPrivate; @@ -136,10 +102,6 @@ G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN) /*****************************************************************************/ -static void _storage_reload_data_head_clear (NMSKeyfileStorage *storage); - -/*****************************************************************************/ - static gboolean _ignore_filename (NMSKeyfileStorageType storage_type, const char *filename) @@ -168,18 +130,19 @@ _path_detect_storage_type (const char *full_filename, NMSKeyfileStorageType *out_storage_type, const char **out_dirname, const char **out_filename, - GError **error) + gboolean *out_is_loaded_uuid_file, + gboolean *out_failed_due_to_invalid_filename) { NMSKeyfileStorageType storage_type; const char *filename = NULL; const char *dirname = NULL; guint i; + gboolean is_loaded_uuid_file = FALSE; - if (full_filename[0] != '/') { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, - "filename is not an absolute path"); + NM_SET_OUT (out_failed_due_to_invalid_filename, FALSE); + + if (full_filename[0] != '/') return FALSE; - } if ( dirname_run && (filename = nm_utils_file_is_in_path (full_filename, dirname_run))) { @@ -192,27 +155,33 @@ _path_detect_storage_type (const char *full_filename, } else { for (i = 0; dirname_libs && dirname_libs[i]; i++) { if ((filename = nm_utils_file_is_in_path (full_filename, dirname_libs[i]))) { - storage_type = NMS_KEYFILE_STORAGE_TYPE_LIB; + storage_type = NMS_KEYFILE_STORAGE_TYPE_LIB (i); dirname = dirname_libs[i]; break; } } - if (!dirname) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, - "filename is not inside a keyfile directory"); + if (!dirname) return FALSE; - } } if (_ignore_filename (storage_type, filename)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, - "filename is not a valid keyfile"); - return FALSE; + + /* we accept loaded-uuid files, but only in /etc and /run directories. */ + + if ( !NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC) + || !nms_keyfile_loaded_uuid_is_filename (filename, NULL)) { + NM_SET_OUT (out_failed_due_to_invalid_filename, TRUE); + return FALSE; + } + + is_loaded_uuid_file = TRUE; } NM_SET_OUT (out_storage_type, storage_type); NM_SET_OUT (out_dirname, dirname); NM_SET_OUT (out_filename, filename); + NM_SET_OUT (out_is_loaded_uuid_file, is_loaded_uuid_file); return TRUE; } @@ -240,118 +209,33 @@ _read_from_file (const char *full_filename, /*****************************************************************************/ -static void -_conn_reload_data_destroy (ConnReloadData *storage_data) -{ - c_list_unlink_stale (&storage_data->crld_lst); - nm_g_object_unref (storage_data->connection); - g_free (storage_data->full_filename); - g_slice_free (ConnReloadData, storage_data); -} - -static ConnReloadData * -_conn_reload_data_new (guint storage_priority, - NMSKeyfileStorageType storage_type, - char *full_filename_take, - NMConnection *connection_take, - NMTernary is_nm_generated_opt, - NMTernary is_volatile_opt, - const struct stat *st) -{ - ConnReloadData *storage_data; - - nm_assert (NM_IN_SET (is_nm_generated_opt, NM_TERNARY_DEFAULT, FALSE, TRUE)); - nm_assert (NM_IN_SET (is_nm_generated_opt, NM_TERNARY_DEFAULT, FALSE, TRUE)); - - storage_data = g_slice_new (ConnReloadData); - *storage_data = (ConnReloadData) { - .storage_type = storage_type, - .storage_priority = storage_priority, - .full_filename = full_filename_take, - .filename = strrchr (full_filename_take, '/') + 1, - .connection = connection_take, - .is_nm_generated_opt = is_nm_generated_opt, - .is_volatile_opt = is_volatile_opt, - }; - if (st) { - storage_data->stat_mtime = st->st_mtim; - storage_data->stat_dev = st->st_dev; - storage_data->stat_ino = st->st_ino; - } - - nm_assert (storage_data->storage_type == storage_type); - nm_assert (storage_data->storage_priority == storage_priority); - nm_assert (storage_data->full_filename); - nm_assert (storage_data->full_filename[0] == '/'); - nm_assert (storage_data->filename); - nm_assert (storage_data->filename[0]); - nm_assert (!strchr (storage_data->filename, '/')); - - return storage_data; -} - -static int -_conn_reload_data_cmp_by_priority (const CList *lst_a, - const CList *lst_b, - const void *user_data) +int +_nms_keyfile_storage_cmp_fcn (NMSKeyfileStorage *a, + NMSKeyfileStorage *b) { - const ConnReloadData *a = c_list_entry (lst_a, ConnReloadData, crld_lst); - const ConnReloadData *b = c_list_entry (lst_b, ConnReloadData, crld_lst); + nm_assert (NMS_IS_KEYFILE_STORAGE (a)); + nm_assert (NMS_IS_KEYFILE_STORAGE (b)); + nm_assert (a != b); - /* we sort more important entries first. */ + /* sort by storage-type, which also has a numberic value according to their + * (inverse) priority. */ + NM_CMP_FIELD_UNSAFE (b, a, storage_type); - /* sorting by storage-priority implies sorting by storage-type too. - * That is, because for different storage-types, we assign different storage-priorities - * and their sort order corresponds (with inverted order). Assert for that. */ - nm_assert ( a->storage_type == b->storage_type - || ( (a->storage_priority != b->storage_priority) - && (a->storage_type < b->storage_type) == (a->storage_priority > b->storage_priority))); - - /* sort by storage-priority, smaller is more important. */ - NM_CMP_FIELD_UNSAFE (a, b, storage_priority); + /* tombstones are more important. */ + nm_assert (a->is_tombstone == nm_settings_storage_is_keyfile_tombstone (NM_SETTINGS_STORAGE (a))); + nm_assert (b->is_tombstone == nm_settings_storage_is_keyfile_tombstone (NM_SETTINGS_STORAGE (b))); + NM_CMP_FIELD_UNSAFE (a, b, is_tombstone); /* newer files are more important. */ NM_CMP_FIELD (b, a, stat_mtime.tv_sec); NM_CMP_FIELD (b, a, stat_mtime.tv_nsec); - NM_CMP_FIELD_STR (a, b, filename); + NM_CMP_DIRECT_STRCMP0 (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b)); nm_assert_not_reached (); return 0; } -/* stat(@loaded_path) and if the path is the same as any of the ones from - * @crld_lst_head, move the found entry to the front and return TRUE. - * Otherwise, do nothing and return FALSE. */ -static gboolean -_conn_reload_data_prioritize_loaded (CList *crld_lst_head, - const char *loaded_path) -{ - ConnReloadData *storage_data; - struct stat st_loaded; - - nm_assert (loaded_path); - nm_assert (!nm_streq (loaded_path, NM_KEYFILE_PATH_NMLOADED_NULL)); - - if (loaded_path[0] != '/') - return FALSE; - - /* we compare the file based on the inode, not based on the path. - * stat() the file. */ - if (stat (loaded_path, &st_loaded) != 0) - return FALSE; - - c_list_for_each_entry (storage_data, crld_lst_head, crld_lst) { - if ( storage_data->stat_dev == st_loaded.st_dev - && storage_data->stat_ino == st_loaded.st_ino) { - nm_c_list_move_front (crld_lst_head, &storage_data->crld_lst); - return TRUE; - } - } - - return FALSE; -} - /*****************************************************************************/ static void @@ -363,9 +247,10 @@ _nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */, nm_assert (!plugin || NMS_IS_KEYFILE_PLUGIN (plugin)); nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); nm_assert (!plugin || plugin == nm_settings_storage_get_plugin (storage)); + nm_assert (!((NMSKeyfileStorage *) storage)->is_tombstone || !(((NMSKeyfileStorage *) storage)->connection)); nm_assert (({ const char *f = nms_keyfile_storage_get_filename (storage); - !f || f[0] == '/'; + f && f[0] == '/'; })); nm_assert (nm_utils_is_uuid (nms_keyfile_storage_get_uuid (storage))); @@ -376,8 +261,8 @@ _nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */, nm_assert ( !tracked || !plugin - || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx, - nms_keyfile_storage_get_uuid (storage))); + || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_filename, + nms_keyfile_storage_get_filename (storage))); #endif } @@ -387,10 +272,9 @@ _nms_keyfile_storage_clear (NMSKeyfileStorage *storage) _nm_assert_storage (NULL, storage, FALSE); c_list_unlink (&storage->storage_lst); + c_list_unlink (&storage->storage_by_uuid_lst); - _storage_reload_data_head_clear (storage); - - g_clear_object (&storage->connection_exported); + g_clear_object (&storage->connection); } static void @@ -402,79 +286,43 @@ _storage_destroy (NMSKeyfileStorage *storage) g_object_unref (storage); } -static ConnReloadHead * -_storage_reload_data_head_ensure (NMSKeyfileStorage *storage) -{ - ConnReloadHead *hd; - - hd = storage->_reload_data_head; - if (!hd) { - hd = g_slice_new (ConnReloadHead); - *hd = (ConnReloadHead) { - .crld_lst_head = C_LIST_INIT (hd->crld_lst_head), - }; - storage->_reload_data_head = hd; - } - - return hd; -} - static void -_storage_reload_data_head_clear (NMSKeyfileStorage *storage) +_storage_copy_content (NMSKeyfileStorage *dst, + NMSKeyfileStorage *src) { - ConnReloadHead *hd; - ConnReloadData *storage_data; - - hd = g_steal_pointer (&storage->_reload_data_head); - if (!hd) - return; - - while ((storage_data = c_list_first_entry (&hd->crld_lst_head, ConnReloadData, crld_lst))) - _conn_reload_data_destroy (storage_data); - - g_free (hd->loaded_path_run); - g_free (hd->loaded_path_etc); - g_slice_free (ConnReloadHead, hd); + _nm_assert_storage (NULL, dst, FALSE); + _nm_assert_storage (NULL, src, FALSE); + nm_assert (src != dst); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (dst), nms_keyfile_storage_get_uuid (src))); + nm_assert (nms_keyfile_storage_get_filename (dst) && nm_streq (nms_keyfile_storage_get_filename (dst), nms_keyfile_storage_get_filename (src))); + + nm_g_object_ref_set (&dst->connection, src->connection); + dst->storage_type = src->storage_type; + dst->stat_mtime = src->stat_mtime; + dst->is_nm_generated = src->is_nm_generated; + dst->is_volatile = src->is_volatile; + dst->is_tombstone = src->is_tombstone; } -static gboolean -_storage_has_equal_connection (NMSKeyfileStorage *storage, - NMConnection *connection) +static NMConnection * +_storage_steal_connection (NMSKeyfileStorage *storage) { - nm_assert (NM_IS_CONNECTION (connection)); - - return storage->connection_exported - && nm_connection_compare (connection, - storage->connection_exported, - NM_SETTING_COMPARE_FLAG_EXACT); -} + nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); + nm_assert (NM_IS_CONNECTION (storage->connection)); -static void -_storage_set_type (NMSKeyfileStorage *storage, - NMSKeyfileStorageType storage_type, - NMTernary is_nm_generated_opt, - NMTernary is_volatile_opt) -{ - storage->storage_type_exported = storage_type; - if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { - storage->is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE); - storage->is_volatile = (is_volatile_opt == NM_TERNARY_TRUE); - } else { - storage->is_nm_generated = FALSE; - storage->is_volatile = FALSE; - } + return g_steal_pointer (&storage->connection); } /*****************************************************************************/ typedef struct { - GHashTable *filename_idx; + GHashTable *idx_by_filename; const char *allowed_filename; } AllowFilenameData; -#define ALLOW_FILENAME_DATA(_filename_idx, _allowed_filename) \ +#define ALLOW_FILENAME_DATA(_idx_by_filename, _allowed_filename) \ (&((AllowFilenameData) { \ - .filename_idx = (_filename_idx), \ + .idx_by_filename = (_idx_by_filename), \ .allowed_filename = (_allowed_filename), \ })) @@ -484,180 +332,149 @@ _allow_filename_cb (const char *filename, { const AllowFilenameData *allow_filename_data = user_data; - if (!g_hash_table_contains (allow_filename_data->filename_idx, filename)) - return TRUE; if ( allow_filename_data->allowed_filename && nm_streq (allow_filename_data->allowed_filename, filename)) return TRUE; - return FALSE; + + return !g_hash_table_contains (allow_filename_data->idx_by_filename, filename); } +/*****************************************************************************/ + static NMSKeyfileStorage * -_storages_get (NMSKeyfilePlugin *self, - const char *uuid, - gboolean create) +_load_file (NMSKeyfilePlugin *self, + const char *dirname, + const char *filename, + NMSKeyfileStorageType storage_type, + GError **error) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - NMSKeyfileStorage *storage; - - nm_assert (uuid && nm_utils_is_uuid (uuid)); - - storage = g_hash_table_lookup (priv->storages.idx, uuid); - if ( !storage - && create) { - storage = nms_keyfile_storage_new (self, uuid); - c_list_link_tail (&priv->storages.lst_head, &storage->storage_lst); - if (!g_hash_table_insert (priv->storages.idx, (char *) nms_keyfile_storage_get_uuid (storage), storage)) - nm_assert_not_reached (); - } - - return storage; -} + NMSKeyfilePluginPrivate *priv; + gs_unref_object NMConnection *connection = NULL; + NMTernary is_volatile_opt; + NMTernary is_nm_generated_opt; + gs_free_error GError *local = NULL; + gs_free char *full_filename = NULL; + struct stat st; -static void -_storages_set_full_filename (NMSKeyfilePluginPrivate *priv, - NMSKeyfileStorage *storage, - char *full_filename_take) -{ - const char *filename; + if (_ignore_filename (storage_type, filename)) { + gs_free char *loaded_uuid = NULL; + gs_free char *loaded_path = NULL; - _nm_assert_storage (NULL, storage, TRUE); - nm_assert (full_filename_take && full_filename_take[0] == '/'); + if (!nms_keyfile_loaded_uuid_read (dirname, + filename, + &full_filename, + &loaded_uuid, + &loaded_path, + NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip due to invalid filename"); + else + _LOGT ("load: \"%s/%s\": skip file due to invalid filename", dirname, filename); + return NULL; + } + if (!NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip loaded uuid file from read-only directory"); + else + _LOGT ("load: \"%s/%s\": skip loaded file from read-only directory", dirname, filename); + return NULL; + } + if (!nm_streq (loaded_path, NM_KEYFILE_PATH_NMLOADED_NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip loaded uuid file not pointing to %s", NM_KEYFILE_PATH_NMLOADED_NULL); + else + _LOGT ("load: \"%s/%s\": skip loaded file pointing not to %s", dirname, filename, NM_KEYFILE_PATH_NMLOADED_NULL); + return NULL; + } - filename = nms_keyfile_storage_get_filename (storage); - if (filename) { - if (!g_hash_table_remove (priv->storages.filename_idx, filename)) - nm_assert_not_reached (); + return nms_keyfile_storage_new_tombstone (self, + loaded_uuid, + full_filename, + storage_type); } - _nm_settings_storage_set_filename_take (NM_SETTINGS_STORAGE (storage), full_filename_take); + full_filename = g_build_filename (dirname, filename, NULL); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + connection = _read_from_file (full_filename, + _get_plugin_dir (priv), + &st, + &is_nm_generated_opt, + &is_volatile_opt, + &local); + if (!connection) { + if (error) + g_propagate_error (error, local); + else + _LOGW ("load: \"%s\": failed to load connection: %s", full_filename, local->message); + return NULL; + } - if (!g_hash_table_insert (priv->storages.filename_idx, - (char *) nms_keyfile_storage_get_filename (storage), - storage)) - nm_assert_not_reached (); + return nms_keyfile_storage_new_connection (self, + g_steal_pointer (&connection), + full_filename, + storage_type, + is_nm_generated_opt, + is_volatile_opt, + &st.st_mtim); } -static void -_storages_remove (NMSKeyfilePluginPrivate *priv, - NMSKeyfileStorage *storage, - gboolean destroy /* or else steal */) +static NMSKeyfileStorage * +_load_file_from_path (NMSKeyfilePlugin *self, + const char *full_filename, + NMSKeyfileStorageType storage_type, + GError **error) { - const char *filename; - - nm_assert (priv); - nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); - nm_assert (c_list_contains (&priv->storages.lst_head, &storage->storage_lst)); - nm_assert (g_hash_table_lookup (priv->storages.idx, nms_keyfile_storage_get_uuid (storage)) == storage); - - c_list_unlink (&storage->storage_lst); - - filename = nms_keyfile_storage_get_filename (storage); - if (filename) { - if (!g_hash_table_remove (priv->storages.filename_idx, filename)) - nm_assert_not_reached (); + gs_free char *f_dirname_free = NULL; + const char *f_filename; + const char *f_dirname; - /* we don't clear the filename of the storage, although we drop it. - * - * Probably nobody cares about the earlier filename at this point, but - * incase someone would (e.g. for logging that the storage was removed), - * it makes sense to keep what was previously. */ - } + nm_assert (full_filename && full_filename[0] == '/'); - if (destroy) { - if (!g_hash_table_remove (priv->storages.idx, nms_keyfile_storage_get_uuid (storage))) - nm_assert_not_reached (); - } else { - if (!g_hash_table_steal (priv->storages.idx, nms_keyfile_storage_get_uuid (storage))) - nm_assert_not_reached (); - } + f_filename = strrchr (full_filename, '/'); + f_dirname = nm_strndup_a (300, full_filename, f_filename - full_filename, &f_dirname_free); + f_filename++; + return _load_file (self, + f_dirname, + f_filename, + storage_type, + error); } -/*****************************************************************************/ - static void _load_dir (NMSKeyfilePlugin *self, - guint storage_priority, NMSKeyfileStorageType storage_type, const char *dirname, - const char *plugin_dir) + NMSettUtilStorages *storages) { const char *filename; GDir *dir; gs_unref_hashtable GHashTable *dupl_filenames = NULL; - if (!dirname) - return; - dir = g_dir_open (dirname, 0, NULL); if (!dir) return; - dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); while ((filename = g_dir_read_name (dir))) { - gs_unref_object NMConnection *connection = NULL; - gs_free_error GError *error = NULL; - NMSKeyfileStorage *storage; - ConnReloadData *storage_data; - gs_free char *full_filename = NULL; - struct stat st; - ConnReloadHead *hd; - NMTernary is_nm_generated_opt; - NMTernary is_volatile_opt; - - if (!g_hash_table_add (dupl_filenames, g_strdup (filename))) { - /* ensure no duplicates. */ - continue; - } - - if (_ignore_filename (storage_type, filename)) { - gs_free char *loaded_uuid = NULL; - gs_free char *loaded_path = NULL; + gs_unref_object NMSKeyfileStorage *storage = NULL; - if (!nms_keyfile_loaded_uuid_read (dirname, - filename, - NULL, - &loaded_uuid, - &loaded_path)) { - _LOGT ("load: \"%s/%s\": skip file due to invalid filename", dirname, filename); - continue; - } - if (!NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, - NMS_KEYFILE_STORAGE_TYPE_ETC)) { - _LOGT ("load: \"%s/%s\": skip loaded file from read-only directory", dirname, filename); - continue; - } - storage = _storages_get (self, loaded_uuid, TRUE); - hd = _storage_reload_data_head_ensure (storage); - if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { - nm_assert (!hd->loaded_path_run); - hd->loaded_path_run = g_steal_pointer (&loaded_path); - } else { - nm_assert (!hd->loaded_path_etc); - hd->loaded_path_etc = g_steal_pointer (&loaded_path); - } + filename = g_strdup (filename); + if (!g_hash_table_add (dupl_filenames, (char *) filename)) continue; - } - full_filename = g_build_filename (dirname, filename, NULL); - - connection = _read_from_file (full_filename, plugin_dir, &st, &is_nm_generated_opt, &is_volatile_opt, &error); - if (!connection) { - _LOGW ("load: \"%s\": failed to load connection: %s", full_filename, error->message); + storage = _load_file (self, + dirname, + filename, + storage_type, + NULL); + if (!storage) continue; - } - storage = _storages_get (self, nm_connection_get_uuid (connection), TRUE); - hd = _storage_reload_data_head_ensure (storage); - storage_data = _conn_reload_data_new (storage_priority, - storage_type, - g_steal_pointer (&full_filename), - g_steal_pointer (&connection), - is_nm_generated_opt, - is_volatile_opt, - &st); - c_list_link_tail (&hd->crld_lst_head, &storage_data->crld_lst); + nm_sett_util_storages_add_take (storages, g_steal_pointer (&storage)); } g_dir_close (dir); @@ -666,367 +483,285 @@ _load_dir (NMSKeyfilePlugin *self, /*****************************************************************************/ static void -reload_connections (NMSettingsPlugin *plugin, - NMSettingsPluginConnectionReloadCallback callback, - gpointer user_data) +_storages_consolidate (NMSKeyfilePlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); - const char *plugin_dir = _get_plugin_dir (priv); gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; NMSKeyfileStorage *storage_safe; NMSKeyfileStorage *storage; guint i; -#if NM_MORE_ASSERTS - c_list_for_each_entry (storage, &priv->storages.lst_head, storage_lst) { - nm_assert (!storage->_reload_data_head); - nm_assert (NM_IS_CONNECTION (storage->connection_exported)); - } -#endif - storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); - _load_dir (self, 1, NMS_KEYFILE_STORAGE_TYPE_RUN, priv->dirname_run, plugin_dir); - _load_dir (self, 2, NMS_KEYFILE_STORAGE_TYPE_ETC, priv->dirname_etc, plugin_dir); - for (i = 0; priv->dirname_libs[i]; i++) - _load_dir (self, 3 + i, NMS_KEYFILE_STORAGE_TYPE_LIB, priv->dirname_libs[i], plugin_dir); + c_list_for_each_entry (storage, &storages_new->lst_head, storage_lst) + storage->dirty = TRUE; - /* process the loaded information. */ c_list_for_each_entry_safe (storage, storage_safe, &priv->storages.lst_head, storage_lst) { - ConnReloadHead *hd; - ConnReloadData *rd, *rd_best; - gboolean connection_modified; - gboolean connection_renamed; - gboolean loaded_path_masked = FALSE; - const char *loaded_dirname = NULL; - gs_free char *loaded_path = NULL; - - hd = storage->_reload_data_head; - - nm_assert (!hd || ( !c_list_is_empty (&hd->crld_lst_head) - || hd->loaded_path_run - || hd->loaded_path_etc)); - nm_assert (hd || nms_keyfile_storage_get_filename (storage)); - - /* find and steal the loaded-path (if any) */ - if (hd) { - if (hd->loaded_path_run) { - if (hd->loaded_path_etc) { - gs_free char *f1 = NULL; - gs_free char *f2 = NULL; - - _LOGT ("load: \"%s\": shadowed by \"%s\"", - (f1 = nms_keyfile_loaded_uuid_filename (priv->dirname_etc, nms_keyfile_storage_get_uuid (storage), FALSE)), - (f2 = nms_keyfile_loaded_uuid_filename (priv->dirname_run, nms_keyfile_storage_get_uuid (storage), FALSE))); - nm_clear_g_free (&hd->loaded_path_etc); - } - loaded_dirname = priv->dirname_run; - loaded_path = g_steal_pointer (&hd->loaded_path_run); - } else if (hd->loaded_path_etc) { - loaded_dirname = priv->dirname_etc; - loaded_path = g_steal_pointer (&hd->loaded_path_etc); - } - } - nm_assert ((!loaded_path) == (!loaded_dirname)); - - /* sort storage datas by priority. */ - if (hd) - c_list_sort (&hd->crld_lst_head, _conn_reload_data_cmp_by_priority, NULL); - - if (loaded_path) { - if (nm_streq (loaded_path, NM_KEYFILE_PATH_NMLOADED_NULL)) { - loaded_path_masked = TRUE; - nm_clear_g_free (&loaded_path); - } else if (!_conn_reload_data_prioritize_loaded (&hd->crld_lst_head, loaded_path)) { - gs_free char *f1 = NULL; - - _LOGT ("load: \"%s\": ignore invalid target \"%s\"", - (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)), - loaded_path); - nm_clear_g_free (&loaded_path); - } - } - - rd_best = hd - ? c_list_first_entry (&hd->crld_lst_head, ConnReloadData, crld_lst) - : NULL; - if ( !rd_best - || loaded_path_masked) { - - /* after reload, no file references this profile (or the files are masked from loading - * via a symlink to /dev/null). */ - - if (_LOGT_ENABLED ()) { - gs_free char *f1 = NULL; - - if (!hd) { - _LOGT ("load: \"%s\": file no longer exists for profile with UUID \"%s\" (remove profile)", - nms_keyfile_storage_get_filename (storage), - nms_keyfile_storage_get_uuid (storage)); - } else if (rd_best) { - c_list_for_each_entry (rd, &hd->crld_lst_head, crld_lst) { - _LOGT ("load: \"%s\": profile %s masked by \"%s\" file symlinking \"%s\"%s", - rd->full_filename, - nms_keyfile_storage_get_uuid (storage), - f1 ?: (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)), - NM_KEYFILE_PATH_NMLOADED_NULL, - storage->connection_exported ? " (remove profile)" : ""); - } - } else if (loaded_path_masked) { - _LOGT ("load: \"%s\": symlinks \"%s\" to hide UUID \"%s\"%s", - (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)), - NM_KEYFILE_PATH_NMLOADED_NULL, - nms_keyfile_storage_get_uuid (storage), - storage->connection_exported ? " (remove profile)" : ""); - } else { - _LOGT ("load: \"%s\": symlinks \"%s\" but there are no profiles with UUID \"%s\"%s", - (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)), - loaded_path, - nms_keyfile_storage_get_uuid (storage), - storage->connection_exported ? " (remove profile)" : ""); - } - } - - if (!storage->connection_exported) - _storages_remove (priv, storage, TRUE); - else { - _storages_remove (priv, storage, FALSE); - c_list_link_tail (&lst_conn_info_deleted, &storage->storage_lst); + gs_unref_object NMSKeyfileStorage *storage_2 = NULL; + gs_unref_object NMSKeyfileStorage *storage_loaded_2 = NULL; + NMSKeyfileStorage *storage_loaded; + + nm_assert (!storage->connection); + + storage_loaded = nm_sett_util_storages_lookup_by_filename (storages_new, nms_keyfile_storage_get_filename (storage)); + + if ( !storage_loaded + || !nm_streq (nms_keyfile_storage_get_uuid (storage), nms_keyfile_storage_get_uuid (storage_loaded))) { + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage))) { + storage_2 = nm_sett_util_storages_steal (&priv->storages, storage); + c_list_link_tail (&storages_deleted, &storage_2->storage_lst); + g_steal_pointer (&storage_2); } continue; } - c_list_for_each_entry (rd, &hd->crld_lst_head, crld_lst) { - if (rd_best != rd) { - _LOGT ("load: \"%s\": profile %s shadowed by \"%s\" file", - rd->full_filename, - nms_keyfile_storage_get_uuid (storage), - rd_best->full_filename); - } - } + storage_2 = nm_sett_util_storages_steal (&priv->storages, storage); - connection_modified = !_storage_has_equal_connection (storage, rd_best->connection); - connection_renamed = !nms_keyfile_storage_get_filename (storage) - || !nm_streq (nms_keyfile_storage_get_filename (storage), rd_best->full_filename); - - { - gs_free char *f1 = NULL; - - _LOGT ("load: \"%s\": profile %s (%s) loaded (%s)" - "%s%s%s" - "%s%s%s", - rd_best->full_filename, - nms_keyfile_storage_get_uuid (storage), - nm_connection_get_id (rd_best->connection), - connection_modified ? (nms_keyfile_storage_get_filename (storage) ? "updated" : "added" ) : "unchanged", - NM_PRINT_FMT_QUOTED (loaded_path, - " (hinted by \"", - (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)), - "\")", - ""), - NM_PRINT_FMT_QUOTED (connection_renamed && nms_keyfile_storage_get_filename (storage), - " (renamed from \"", - nms_keyfile_storage_get_filename (storage), - "\")", - "")); - } - - _storage_set_type (storage, rd_best->storage_type, rd_best->is_nm_generated_opt, rd_best->is_volatile_opt); + _storage_copy_content (storage_2, storage_loaded); - if (connection_modified) - nm_g_object_ref_set (&storage->connection_exported, rd_best->connection); + storage_2->dirty = FALSE; - _storages_set_full_filename (priv, - storage, - g_steal_pointer (&rd_best->full_filename)); + storage_loaded_2 = nm_sett_util_storages_steal_and_replace (storages_new, storage_loaded, storage_2); - /* we don't need the reload data anymore. Drop it. */ - _storage_reload_data_head_clear (storage); + g_ptr_array_add (storages_modified, g_steal_pointer (&storage_2)); + } - g_ptr_array_add (storages_modified, g_object_ref (storage)); + c_list_for_each_entry (storage, &storages_new->lst_head, storage_lst) { + if (storage->dirty) + g_ptr_array_add (storages_modified, g_object_ref (storage)); + else + storage->dirty = TRUE; } - /* raise events. */ + nm_sett_util_storages_swap (&priv->storages, storages_new); - c_list_for_each_entry_safe (storage, storage_safe, &lst_conn_info_deleted, storage_lst) { - callback (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_STORAGE (storage), - NULL, - user_data); - _storage_destroy (storage); - } + /* raise events. */ for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; storage = storages_modified->pdata[i]; - if (storage != _storages_get (self, nms_keyfile_storage_get_uuid (storage), FALSE)) { + if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage))) { /* hm? The profile was deleted in the meantime? That is only possible * if the signal handler called again into the plugin. In any case, the event * was already emitted. Skip. */ continue; } + if (!storage->dirty) { + /* the entry is no longer dirty. In the meantime we already emited + * another signal for it. */ + continue; + } - nm_assert (NM_IS_CONNECTION (storage->connection_exported)); + storage->dirty = FALSE; + + connection = _storage_steal_connection (storage); callback (NM_SETTINGS_PLUGIN (self), NM_SETTINGS_STORAGE (storage), - storage->connection_exported, + connection, user_data); } + + while ((storage = c_list_first_entry (&storages_deleted, NMSKeyfileStorage, storage_lst))) { + c_list_unlink (&storage->storage_lst); + storage->is_tombstone = FALSE; + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + _storage_destroy (storage); + } } -static gboolean -load_connection (NMSettingsPlugin *plugin, - const char *full_filename, - NMSettingsStorage **out_storage, - NMConnection **out_connection, - NMSettingsStorage **out_storage_replaced, - GError **error) +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - NMSKeyfileStorageType storage_type; - const char *f_filename; - const char *f_dirname; - const char *uuid; - NMSKeyfileStorage *storage_by_filename; - NMSKeyfileStorage *storage = NULL; - gs_unref_object NMConnection *connection_new = NULL; - gboolean connection_modified; - gboolean connection_renamed; - gs_free_error GError *local = NULL; - gboolean loaded_uuid_success; - gs_free char *loaded_uuid_filename = NULL; - NMTernary is_nm_generated_opt; - NMTernary is_volatile_opt; + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, _storage_destroy); + int i; - nm_assert (out_storage && !*out_storage); - nm_assert (out_connection && !*out_connection); - nm_assert (out_storage_replaced && !*out_storage_replaced); - - if (!_path_detect_storage_type (full_filename, - (const char *const*) priv->dirname_libs, - priv->dirname_etc, - priv->dirname_run, - &storage_type, - &f_dirname, - &f_filename, - error)) - return FALSE; + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_RUN, priv->dirname_run, &storages_new); + if (priv->dirname_etc) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_ETC, priv->dirname_etc, &storages_new); + for (i = 0; priv->dirname_libs[i]; i++) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_LIB (i), priv->dirname_libs[i], &storages_new); + + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); +} + +static void +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, _storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; - storage_by_filename = g_hash_table_lookup (priv->storages.filename_idx, full_filename); + if (n_entries == 0) + return; - connection_new = _read_from_file (full_filename, _get_plugin_dir (priv), NULL, &is_nm_generated_opt, &is_volatile_opt, &local); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - if (!connection_new) { - struct stat st; + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); - if ( storage_by_filename - && stat (full_filename, &st) != 0 - && errno == ENOENT) { + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); - /* If the file no longer exists, we accept that as command to remove the connection. - * - * That goes in line with reloading ifcfg-file files that now have NM_CONTROLLED=no. - * That will unload the ifcfg file. Likewise, removing the keyfile and loading - * the removed path will drop the connection as well. - * - * In other cases, a loading error will not unload an existing connection. Imagine - * you edit the file and have a typo. Then load will simply fail and not removing - * the profile. */ + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + NMSKeyfileStorageType storage_type; + gs_free_error GError *local = NULL; + const char *f_filename; + const char *f_dirname; + const char *full_filename; + gs_free char *full_filename_keep = NULL; + gboolean is_loaded_uuid_file; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gboolean failed_due_to_invalid_filename; + gs_unref_object NMSKeyfileStorage *storage = NULL; - *out_storage_replaced = g_object_ref (NM_SETTINGS_STORAGE (storage_by_filename)); + if (entry->handled) + continue; - _LOGT ("load: \"%s\": unload previously loaded connection %s (file no longer exists)", - full_filename, - nms_keyfile_storage_get_uuid (storage_by_filename)); + if (!_path_detect_storage_type (entry->filename, + (const char *const*) priv->dirname_libs, + priv->dirname_etc, + priv->dirname_run, + &storage_type, + &f_dirname, + &f_filename, + &is_loaded_uuid_file, + &failed_due_to_invalid_filename)) { + if (failed_due_to_invalid_filename) { + entry->handled = TRUE; + nm_utils_error_set (&entry->error, NM_UTILS_ERROR_UNKNOWN, "filename is not valid for a keyfile"); + } + continue; + } - _storages_remove (priv, storage_by_filename, TRUE); + full_filename_keep = g_build_filename (f_dirname, f_filename, NULL); - /* Despite we did not *load* anything, we successfully *unloaded*. Return success. */ - return TRUE; + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; } - _LOGT ("load: \"%s\": failed to load connection: %s", full_filename, local->message); - g_propagate_error (error, g_steal_pointer (&local)); - return FALSE; - } + entry->handled = TRUE; - uuid = nm_connection_get_uuid (connection_new); + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); - if ( storage_by_filename - && nm_streq (uuid, nms_keyfile_storage_get_uuid (storage_by_filename))) { - nm_assert (storage_by_filename == _storages_get (self, uuid, FALSE)); - storage = g_steal_pointer (&storage_by_filename); - } else - storage = _storages_get (self, uuid, TRUE); - - connection_modified = !_storage_has_equal_connection (storage, connection_new); - connection_renamed = !nms_keyfile_storage_get_filename (storage) - || !nm_streq (nms_keyfile_storage_get_filename (storage), full_filename); - - /* FIXME(settings-rework): we now always mark the loaded profile via a symlink - * and even for deleted profiles, we remember that they got deleted via a symlink - * to /dev/null. - * The problem is that these symlinks don't get garbage collected. It'complicated - * to understand when a symlink is really no longer needed and when no other profile - * references it. - * - * I think the only solution here is to periodically load all files (temporarily, - * without actually using them) to find the existing files and UUIDs. - * And then prune the symlinks for UUIDs that are no longer in use. - * - * This periodic cleanup should also be used to prune /var/lib/NetworkManager/timestamps - * and /var/lib/NetworkManager/seen-bssids. */ - loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run, - uuid, - full_filename, - TRUE, - &loaded_uuid_filename); - - _LOGT ("load: \"%s/%s\": profile %s (%s) loaded (%s)" - "%s%s%s" - "%s%s%s" - " (%s%s%s)", - f_dirname, - f_filename, - nms_keyfile_storage_get_uuid (storage), - nm_connection_get_id (connection_new), - connection_modified ? (storage->connection_exported ? "updated" : "added" ) : "unchanged", - NM_PRINT_FMT_QUOTED (connection_renamed && nms_keyfile_storage_get_filename (storage), - " (renamed from \"", - nms_keyfile_storage_get_filename (storage), - "\")", - ""), - NM_PRINT_FMT_QUOTED (storage_by_filename, - " (replaces ", - nms_keyfile_storage_get_uuid (storage_by_filename), - ")", - ""), - NM_PRINT_FMT_QUOTED (loaded_uuid_success, - "marked as preferred by \"", - loaded_uuid_filename, - "\"", - "failed to write loaded file")); - - _storage_set_type (storage, storage_type, is_nm_generated_opt, is_volatile_opt); - - if (connection_renamed) { - _storages_set_full_filename (priv, - storage, - g_build_filename (f_dirname, f_filename, NULL)); + storage = _load_file (self, + f_dirname, + f_filename, + storage_type, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSKeyfileStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; + } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; + } + + g_hash_table_add (loaded_uuids, (char *) nms_keyfile_storage_get_uuid (storage)); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } - if (connection_modified) - nm_g_object_ref_set (&storage->connection_exported, connection_new); + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSKeyfileStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; - *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage)); - *out_connection = g_object_ref (storage->connection_exported); - *out_storage_replaced = nm_g_object_ref (NM_SETTINGS_STORAGE (storage_by_filename)); + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; - return TRUE; + c_list_for_each_entry (storage, &sbuh->lst_by_uuid_head, storage_by_uuid_lst) { + const char *full_filename = nms_keyfile_storage_get_filename (storage); + gs_unref_object NMSKeyfileStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file_from_path (self, + full_filename, + storage->storage_type, + &local); + if ( storage_new + && !nm_streq (loaded_uuid, nms_keyfile_storage_get_uuid (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } + + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); + } + } + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } gboolean @@ -1041,13 +776,13 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, { NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); gs_unref_object NMConnection *reread = NULL; - gs_free char *loaded_uuid_filename = NULL; gs_free char *full_filename = NULL; NMSKeyfileStorageType storage_type; - gboolean loaded_uuid_success; - NMSKeyfileStorage *storage; + gs_unref_object NMSKeyfileStorage *storage = NULL; GError *local = NULL; const char *uuid; + gboolean reread_same; + struct timespec mtime; nm_assert (NM_IS_CONNECTION (connection)); nm_assert (out_storage && !*out_storage); @@ -1055,16 +790,6 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, uuid = nm_connection_get_uuid (connection); - if (_storages_get (self, uuid, FALSE)) { - _LOGT ("add: %s, \"%s\": failed to add connection that already exists", - uuid, - nm_connection_get_id (connection)); - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "connection with UUID %s already exists", - uuid); - return FALSE; - } - storage_type = !in_memory && priv->dirname_etc ? NMS_KEYFILE_STORAGE_TYPE_ETC : NMS_KEYFILE_STORAGE_TYPE_RUN; @@ -1080,12 +805,12 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, FALSE, FALSE, _allow_filename_cb, - ALLOW_FILENAME_DATA (priv->storages.filename_idx, NULL), + ALLOW_FILENAME_DATA (priv->storages.idx_by_filename, NULL), &full_filename, &reread, - NULL, + &reread_same, &local)) { - _LOGT ("add: %s, \"%s\": failed to add connection: %s", + _LOGT ("commit: %s (%s) failed to add: %s", nm_connection_get_uuid (connection), nm_connection_get_id (connection), local->message); @@ -1093,46 +818,35 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, return FALSE; } + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); nm_assert (nm_streq0 (nm_connection_get_uuid (connection), nm_connection_get_uuid (reread))); - uuid = nm_connection_get_uuid (reread); - nm_assert (full_filename && full_filename[0] == '/'); - nm_assert (!_storages_get (self, uuid, FALSE)); - nm_assert (!g_hash_table_contains (priv->storages.filename_idx, full_filename)); + nm_assert (!nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); - loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run, - uuid, - full_filename, - TRUE, - &loaded_uuid_filename); - - _LOGT ("add: \"%s\": profile %s (%s) written" - " (%s%s%s)", - full_filename, + _LOGT ("commit: %s (%s) added as \"%s\" (%s%s)", uuid, nm_connection_get_id (connection), - NM_PRINT_FMT_QUOTED (loaded_uuid_success, - "indicated by \"", - loaded_uuid_filename, - "\"", - "failed to write loaded file")); - - storage = _storages_get (self, uuid, TRUE); - - nm_assert (!storage->connection_exported); - - _storage_set_type (storage, storage_type, is_nm_generated, is_volatile); + full_filename, + is_nm_generated ? "G" : "g", + is_volatile ? "V" : "v"); - storage->connection_exported = g_object_ref (reread); + storage = nms_keyfile_storage_new_connection (self, + g_steal_pointer (&reread), + full_filename, + storage_type, + is_nm_generated ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + is_volatile ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); - _storages_set_full_filename (priv, - storage, - g_steal_pointer (&full_filename)); + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); - *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage)); - *out_connection = g_steal_pointer (&reread); + *out_connection = _storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); return TRUE; } @@ -1169,32 +883,24 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, NMSKeyfileStorage *storage = NMS_KEYFILE_STORAGE (storage_x); gs_unref_object NMConnection *connection_clone = NULL; gs_unref_object NMConnection *reread = NULL; - const char *previous_filename = NULL; - const char *uuid; gs_free char *full_filename = NULL; - gboolean loaded_uuid_success; - gs_free char *loaded_uuid_filename = NULL; - NMSKeyfileStorageType storage_type; - gboolean connection_modified; - gboolean connection_renamed; - GError *local = NULL; + gs_free_error GError *local = NULL; + struct timespec mtime; + const char *previous_filename; + gboolean reread_same; + const char *uuid; _nm_assert_storage (self, storage, TRUE); nm_assert (NM_IS_CONNECTION (connection)); - nm_assert (nm_connection_verify (connection, NULL)); - nm_assert (!error || !*error); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); nm_assert (nm_streq (nms_keyfile_storage_get_uuid (storage), nm_connection_get_uuid (connection))); - nm_assert ( storage->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN - || ( !is_nm_generated - && !is_volatile)); - - if (!priv->dirname_etc) - storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; - else { - storage_type = storage->storage_type_exported; - if (storage_type == NMS_KEYFILE_STORAGE_TYPE_LIB) - storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; - } + nm_assert (!error || !*error); + nm_assert (NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + nm_assert ( (!is_nm_generated && !is_volatile) + || storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN); + nm_assert ( priv->dirname_etc + || storage->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC); previous_filename = nms_keyfile_storage_get_filename (storage); uuid = nms_keyfile_storage_get_uuid (storage); @@ -1202,67 +908,49 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, if (!nms_keyfile_writer_connection (connection, is_nm_generated, is_volatile, - storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC ? priv->dirname_etc : priv->dirname_run, _get_plugin_dir (priv), previous_filename, - (storage_type != storage->storage_type_exported), - force_rename, + FALSE, + FALSE, _allow_filename_cb, - ALLOW_FILENAME_DATA (priv->storages.filename_idx, previous_filename), + ALLOW_FILENAME_DATA (priv->storages.idx_by_filename, previous_filename), &full_filename, &reread, - NULL, + &reread_same, &local)) { - _LOGW ("commit: failure to write %s (%s) to disk: %s", + _LOGW ("commit: failure to write %s (%s) to \"%s\": %s", uuid, nm_connection_get_id (connection_clone), + previous_filename, local->message); - g_propagate_error (error, local); + g_propagate_error (error, g_steal_pointer (&local)); return FALSE; } - loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run, - uuid, - full_filename, - TRUE, - &loaded_uuid_filename); + nm_assert ( full_filename + && nm_streq (full_filename, previous_filename)); - connection_modified = !_storage_has_equal_connection (storage, connection); - connection_renamed = !nm_streq (previous_filename, full_filename); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); + + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nm_connection_get_uuid (reread), uuid)); - _LOGT ("commit: \"%s\": profile %s (%s) written%s" - "%s%s%s" - " (%s%s%s)", + _LOGT ("commit: \"%s\": profile %s (%s) written", full_filename, uuid, - nm_connection_get_id (connection), - connection_modified ? " (modified)" : "", - NM_PRINT_FMT_QUOTED (connection_renamed, - " (renamed from \"", - previous_filename, - "\")", - ""), - NM_PRINT_FMT_QUOTED (loaded_uuid_success, - "indicated by \"", - loaded_uuid_filename, - "\"", - "failed to write loaded file")); - - storage->storage_type_exported = storage_type; - - if (connection_renamed) { - _storages_set_full_filename (priv, - storage, - g_steal_pointer (&full_filename)); - } + nm_connection_get_id (connection)); - if (connection_modified) - nm_g_object_ref_set (&storage->connection_exported, connection); + storage->is_nm_generated = is_nm_generated; + storage->is_volatile = is_volatile; + storage->stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage)); - *out_connection = g_object_ref (storage->connection_exported); + *out_connection = g_steal_pointer (&reread); return TRUE; } @@ -1288,18 +976,16 @@ update_connection (NMSettingsPlugin *plugin, static gboolean delete_connection (NMSettingsPlugin *plugin, NMSettingsStorage *storage_x, - gboolean remove_from_disk, GError **error) { NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); gs_unref_object NMSKeyfileStorage *storage = g_object_ref (NMS_KEYFILE_STORAGE (storage_x)); - gboolean loaded_uuid_success; - gs_free char *loaded_uuid_filename = NULL; const char *remove_from_disk_errmsg = NULL; const char *operation_message; const char *previous_filename; const char *uuid; + gboolean success = TRUE; _nm_assert_storage (self, storage, TRUE); nm_assert (!error || !*error); @@ -1307,45 +993,179 @@ delete_connection (NMSettingsPlugin *plugin, previous_filename = nms_keyfile_storage_get_filename (storage); uuid = nms_keyfile_storage_get_uuid (storage); - if (!remove_from_disk) - operation_message = "only dropped from memory"; - else if (!NM_IN_SET (storage->storage_type_exported, NMS_KEYFILE_STORAGE_TYPE_ETC, - NMS_KEYFILE_STORAGE_TYPE_RUN)) + if (!NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)) { + nm_utils_error_set (error, + NM_UTILS_ERROR_UNKNOWN, + "profile in read-only storage cannot be deleted"); + success = FALSE; operation_message = "dropped readonly file from memory"; - else if (unlink (previous_filename) != 0) { + } else if (unlink (previous_filename) != 0) { int errsv; errsv = errno; if (errsv != ENOENT) { remove_from_disk_errmsg = nm_strerror_native (errsv); operation_message = "failed to delete from disk"; + success = FALSE; + nm_utils_error_set_errno (error, + errsv, + "failure to delete \"%s\": %s", + previous_filename); } else operation_message = "does not exist on disk"; } else operation_message = "deleted from disk"; - loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run, - uuid, - NM_KEYFILE_PATH_NMLOADED_NULL, - FALSE, - &loaded_uuid_filename); - - _LOGT ("delete: %s: profile %s %s" - "%s%s%s" - " (%s%s%s)", + _LOGT ("commit: deleted \"%s\", %s %s (%s%s%s%s)", previous_filename, + storage->is_tombstone ? "tombstone" : "profile", uuid, operation_message, - NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, " (", remove_from_disk_errmsg, ")", ""), - NM_PRINT_FMT_QUOTED (loaded_uuid_success, - "indicated by \"", - loaded_uuid_filename, - "\"", - "failed to write loaded file")); + NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, ": ", remove_from_disk_errmsg, "", "")); - _storages_remove (priv, storage, TRUE); + if (success) { + nm_sett_util_storages_steal (&priv->storages, storage); + storage->is_tombstone = FALSE; + g_object_unref (storage); + } - return TRUE; + return success; +} + +/** + * nms_keyfile_plugin_add_loaded_uuid: + * @self: the #NMSKeyfilePlugin instance + * @simulate: if %TRUE, don't do anything on the filename but just pretend + * that the loaded UUID file gets tracked/untracked. In this mode, the function + * cannot fail (except on hard-failure, see below). + * The idea is that you first try without simulate to write to disk. + * If that fails, you might still want to forcefully pretend (in-memory + * only) that this loaded-uuid is as desired. So you repeate the call + * with @simulate %TRUE. + * @uuid: the UUID for which to write/delete the loaded-uuid file + * @in_memory: the storage type, either /etc or /run. Note that if @self + * has no /etc directory configured, this results in a hard failure. + * @set: if %TRUE, write the symlink to point to /dev/null. If %FALSE, + * delete the symlink (if it exists). + * @out_storage: (transfer full) (allow-none): the storage element that changes, or + * NULL if nothing changed. Note that the file on disk is already as + * we want to write it, then this counts as a change. No change only + * means if we try to delete a storage (@set %FALSE) that did not + * exist previously. + * @out_hard_failure: (allow-none): on failure, indicate that this is a hard failure. + * + * The function writes or deletes symlinks from filesystem. + * + * A hard failure can only happen if @self has no /etc directory configured + * and @in_memory is FALSE. In such case even @simulate call fails (which + * otherwise would always succeed). + * Also, if yet get a hard-failure (with @simulate %FALSE) thre is no point + * in retrying with @simulate %TRUE (contrary to other cases!). + * + * Returns: %TRUE on success. + */ +gboolean +nms_keyfile_plugin_update_loaded_uuid (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure) +{ + NMSKeyfilePluginPrivate *priv; + gboolean hard_failure = FALSE; + NMSKeyfileStorage *storage; + gs_unref_object NMSKeyfileStorage *storage_result = NULL; + gboolean loaded_uuid_success = FALSE; + gs_free char *loaded_uuid_filename = NULL; + NMSKeyfileStorageType storage_type; + const char *loaded_path; + const char *dirname; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (self)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (!out_storage || !*out_storage); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + loaded_path = set + ? NM_KEYFILE_PATH_NMLOADED_NULL + : NULL; + + if (in_memory) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; + dirname = priv->dirname_run; + } else { + if (!priv->dirname_etc) { + _LOGT ("commit: cannot %s%s loaded uuid symlink for %s as there is no /etc directory", + simulate ? "simulate " : "", + loaded_path ? "write" : "delete", + uuid); + hard_failure = TRUE; + goto out; + } + storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; + dirname = priv->dirname_etc; + } + + if (simulate) { + loaded_uuid_success = TRUE; + loaded_uuid_filename = nms_keyfile_loaded_uuid_filename (dirname, uuid, FALSE); + } else { + loaded_uuid_success = nms_keyfile_loaded_uuid_write (dirname, + uuid, + loaded_path, + FALSE, + &loaded_uuid_filename); + } + + _LOGT ("commit: %s symlink \"%s\"%s%s%s %s", + loaded_path ? "writing" : "deleting", + loaded_uuid_filename, + NM_PRINT_FMT_QUOTED (loaded_path, " (pointing to \"", loaded_path, "\")", ""), + simulate + ? "simulated" + : ( loaded_uuid_success + ? "succeeded" + : "failed")); + + if (!loaded_uuid_success) + goto out; + + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, loaded_uuid_filename); + + nm_assert ( !storage + || ( storage->is_tombstone + && storage->storage_type == storage_type + && nm_streq (nms_keyfile_storage_get_uuid (storage), uuid))); + + if (loaded_path) { + + if (!storage) { + storage = nms_keyfile_storage_new_tombstone (self, + uuid, + loaded_uuid_filename, + storage_type); + nm_sett_util_storages_add_take (&priv->storages, storage); + } + + storage_result = g_object_ref (storage); + } else { + if (storage) { + storage_result = nm_sett_util_storages_steal (&priv->storages, storage); + storage_result->is_tombstone = FALSE; + } + } + +out: + nm_assert (!loaded_uuid_success || !hard_failure); + nm_assert (loaded_uuid_success || !storage_result); + + NM_SET_OUT (out_hard_failure, hard_failure); + NM_SET_OUT (out_storage, (NMSettingsStorage *) g_steal_pointer (&storage_result)); + return loaded_uuid_success; } /*****************************************************************************/ @@ -1389,12 +1209,7 @@ nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin) priv->config = g_object_ref (nm_config_get ()); - c_list_init (&priv->storages.lst_head); - priv->storages.filename_idx = g_hash_table_new (nm_str_hash, g_str_equal); - priv->storages.idx = g_hash_table_new_full (nm_str_hash, - g_str_equal, - NULL, - (GDestroyNotify) _storage_destroy); + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, _storage_destroy); /* dirname_libs are a set of read-only directories with lower priority than /etc or /run. * There is nothing complicated about having multiple of such directories, so dirname_libs @@ -1471,8 +1286,7 @@ dispose (GObject *object) if (priv->config) g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy); - nm_clear_pointer (&priv->storages.idx, g_hash_table_destroy); + nm_sett_util_storages_clear (&priv->storages); nm_clear_g_free (&priv->dirname_libs[0]); nm_clear_g_free (&priv->dirname_etc); @@ -1495,7 +1309,7 @@ nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *klass) plugin_class->plugin_name = "keyfile"; plugin_class->get_unmanaged_specs = get_unmanaged_specs; plugin_class->reload_connections = reload_connections; - plugin_class->load_connection = load_connection; + plugin_class->load_connections = load_connections; plugin_class->add_connection = add_connection; plugin_class->update_connection = update_connection; plugin_class->delete_connection = delete_connection; diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h index 7d5b762285..7e4013312a 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h @@ -24,6 +24,8 @@ #include "settings/nm-settings-plugin.h" #include "settings/nm-settings-storage.h" +#include "nms-keyfile-utils.h" + #define NMS_TYPE_KEYFILE_PLUGIN (nms_keyfile_plugin_get_type ()) #define NMS_KEYFILE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePlugin)) #define NMS_KEYFILE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePluginClass)) @@ -57,4 +59,12 @@ gboolean nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, NMConnection **out_connection, GError **error); +gboolean nms_keyfile_plugin_update_loaded_uuid (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure); + #endif /* __NMS_KEYFILE_PLUGIN_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c index b3786c1286..834e8b2b88 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-storage.c +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c @@ -22,6 +22,7 @@ #include "nms-keyfile-storage.h" #include "nm-utils.h" +#include "nm-core-internal.h" #include "nms-keyfile-plugin.h" /*****************************************************************************/ @@ -38,21 +39,84 @@ static void nms_keyfile_storage_init (NMSKeyfileStorage *self) { c_list_init (&self->storage_lst); + c_list_init (&self->storage_by_uuid_lst); } -NMSKeyfileStorage * -nms_keyfile_storage_new (NMSKeyfilePlugin *plugin, - const char *uuid) +static NMSKeyfileStorage * +_storage_new (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename) { nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); return g_object_new (NMS_TYPE_KEYFILE_STORAGE, NM_SETTINGS_STORAGE_PLUGIN, plugin, NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, NULL); } +NMSKeyfileStorage * +nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *self, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type) +{ + NMSKeyfileStorage *storage; + + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + nm_assert (nms_keyfile_loaded_uuid_is_filename (filename, NULL)); + nm_assert (NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + + storage = _storage_new (self, uuid, filename); + + storage->is_tombstone = TRUE; + + storage->storage_type = storage_type; + + return storage; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_connection (NMSKeyfilePlugin *self, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const struct timespec *stat_mtime) +{ + NMSKeyfileStorage *storage; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (self)); + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (filename && filename[0] == '/'); + nm_assert ( storage_type >= NMS_KEYFILE_STORAGE_TYPE_RUN + && storage_type <= _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST); + nmtst_connection_assert_unchanging (connection_take); + + storage = _storage_new (self, nm_connection_get_uuid (connection_take), filename); + + storage->connection = connection_take; /* take reference. */ + + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + storage->is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE); + storage->is_volatile = (is_volatile_opt == NM_TERNARY_TRUE); + } + + if (stat_mtime) + storage->stat_mtime = *stat_mtime; + + storage->storage_type = storage_type; + + return storage; +} + static void dispose (GObject *object) { @@ -67,8 +131,11 @@ static void nms_keyfile_storage_class_init (NMSKeyfileStorageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) _nms_keyfile_storage_cmp_fcn; } /*****************************************************************************/ @@ -90,7 +157,7 @@ nm_settings_storage_load_sett_flags (NMSettingsStorage *self, return; s = NMS_KEYFILE_STORAGE (self); - if (s->storage_type_exported != NMS_KEYFILE_STORAGE_TYPE_RUN) + if (s->storage_type != NMS_KEYFILE_STORAGE_TYPE_RUN) return; if (s->is_nm_generated) diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h index 0f0e7fdc70..7a3629b7d4 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-storage.h +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h @@ -38,25 +38,44 @@ typedef struct { CList storage_lst; - /* Keep a reference of the exported connection. We only need this during - * reload and load, to check whether the content on disk changed. */ - NMConnection *connection_exported; - - /* Only relevant during reload-all. It contains a list of all the files - * that are associated with this UUID. In general, any number of - * files can have connection profiles for a particular UUID. During - * _do_reload_all(), we need to load all of them and find the best one. - * - * This is to simplify reload, because we can figure out whether a UUID - * was added, unchanged, or removed by reusing the list of tracked storages. + CList storage_by_uuid_lst; + + /* The connection. Note that there are tombstones (loaded-uuid files to /dev/null) + * that don't have a connection. * - * After reload-all, this list is cleared again. */ - struct _NMSKeyfileConnReloadHead *_reload_data_head; + * Also, we don't actually remember the loaded connection after returning it + * to NMSettings. So, also for regular storages (non-tombstones) this field + * is often cleared. */ + NMConnection *connection; + + NMSKeyfileStorageType storage_type; - NMSKeyfileStorageType storage_type_exported; + /* the timestamp (stat's mtime) of the keyfile. For tombstones this + * is irrelevant. The purpose is that if the same storage type (directory) has + * multiple files with the same UUID, then the newer file gets preferred. */ + struct timespec stat_mtime; + /* these flags are only relevant for storages with %NMS_KEYFILE_STORAGE_TYPE_RUN + * (and non-tombstones). This is to persist and reload these settings flags to + * /run. */ bool is_nm_generated:1; bool is_volatile:1; + + /* whether this is a tombstone to hide a UUID (via the loaded uuid symlinks). + * If this is falls, the storage contains a profile, though note that + * the connection field will be cleared when it's not used. So, a non-tombstone + * has a connection in principle, but the connection field may still be %NULL. + * + * Note that a tombstone instance doesn't have a connection, but NMSettings + * considers it alive because is_tombstone is %TRUE. That means, once a tombstone + * gets removed, this flag is cleared. Then the storage instance has no connnection + * and is no longer a tombstone, and NMSettings considers it ready for deletion. + */ + bool is_tombstone:1; + + /* this flag is only used during reload to mark and prune old entries. */ + bool dirty:1; + } NMSKeyfileStorage; typedef struct _NMSKeyfileStorageClass NMSKeyfileStorageClass; @@ -65,50 +84,70 @@ GType nms_keyfile_storage_get_type (void); struct _NMSKeyfilePlugin; -NMSKeyfileStorage *nms_keyfile_storage_new (struct _NMSKeyfilePlugin *plugin, - const char *uuid); +NMSKeyfileStorage *nms_keyfile_storage_new_tombstone (struct _NMSKeyfilePlugin *self, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type); + +NMSKeyfileStorage *nms_keyfile_storage_new_connection (struct _NMSKeyfilePlugin *self, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const struct timespec *stat_mtime); void _nms_keyfile_storage_clear (NMSKeyfileStorage *storage); +int _nms_keyfile_storage_cmp_fcn (NMSKeyfileStorage *a, + NMSKeyfileStorage *b); + /*****************************************************************************/ static inline const char * nms_keyfile_storage_get_uuid (NMSKeyfileStorage *self) { - nm_assert (NMS_IS_KEYFILE_STORAGE (self)); - - return nm_settings_storage_get_uuid (NM_SETTINGS_STORAGE (self)); + return nm_settings_storage_get_uuid ((NMSettingsStorage *) self); } static inline const char * nms_keyfile_storage_get_filename (NMSKeyfileStorage *self) { - nm_assert (NMS_IS_KEYFILE_STORAGE (self)); - - return nm_settings_storage_get_filename (NM_SETTINGS_STORAGE (self)); + return nm_settings_storage_get_filename ((NMSettingsStorage *) self); } /*****************************************************************************/ static inline gboolean -nm_settings_storage_is_keyfile (NMSettingsStorage *self, - gboolean *out_is_in_memory) +nm_settings_storage_is_keyfile_run (NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN); +} + +static inline gboolean +nm_settings_storage_is_keyfile_lib (NMSettingsStorage *self) { - if (NMS_IS_KEYFILE_STORAGE (self)) { - NM_SET_OUT (out_is_in_memory, (((NMSKeyfileStorage *) self)->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN)); - return TRUE; - } - NM_SET_OUT (out_is_in_memory, FALSE); - return FALSE; + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type >= NMS_KEYFILE_STORAGE_TYPE_LIB_BASE); } static inline gboolean -nm_settings_storage_is_in_memory (NMSettingsStorage *self) +nm_settings_storage_is_keyfile_tombstone (NMSettingsStorage *self) { + /* Only keyfile storage supports tombstones. They indicate that a uuid + * is shadowed via a symlink to /dev/null. + * + * Note that tombstones don't have a NMConnection instead they shadow + * a UUID. As such, NMSettings considers them alive also if they have + * not profile. That means, when a tombstone gets removed for good, + * the is_tombstone must be cleared (so that it becomes truly dead). */ return NMS_IS_KEYFILE_STORAGE (self) - && (((NMSKeyfileStorage *) self)->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN); + && ((NMSKeyfileStorage *) self)->is_tombstone; } +/*****************************************************************************/ + enum _NMSettingsConnectionIntFlags; void nm_settings_storage_load_sett_flags (NMSettingsStorage *self, diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c index af0dcc56d1..dcc28bc533 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -33,6 +33,60 @@ /*****************************************************************************/ +const char * +nms_keyfile_loaded_uuid_is_filename (const char *filename, + guint *out_uuid_len) +{ + const char *uuid; + const char *tmp; + gsize len; + gs_free char *ln = NULL; + const char *s; + + s = strrchr (filename, '/'); + if (s) + filename = &s[1]; + + if (filename[0] != '.') { + /* the hidden-uuid filename must start with '.'. That is, + * so that it does not conflict with regular keyfiles according + * to nm_keyfile_utils_ignore_filename(). */ + return NULL; + } + + len = strlen (filename); + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED) + || memcmp (filename, NM_KEYFILE_PATH_PREFIX_NMLOADED, NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)) != 0) { + /* the filename does not have the right prefix. */ + return NULL; + } + + tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; + len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); + + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) + || memcmp (&tmp[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)], + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) != 0) { + /* the file does not have the right suffix. */ + return NULL; + } + len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + + if (!NM_IN_SET (len, 36, 40)) { + /* the remaining part of the filename has not the right length to + * contain a UUID (according to nm_utils_is_uuid()). */ + return NULL; + } + + uuid = nm_strndup_a (100, tmp, len, NULL); + if (!nm_utils_is_uuid (uuid)) + return NULL; + + NM_SET_OUT (out_uuid_len, len); + return tmp; +} + char * nms_keyfile_loaded_uuid_filename (const char *dirname, const char *uuid, @@ -65,58 +119,26 @@ nms_keyfile_loaded_uuid_read (const char *dirname, const char *filename, char **out_full_filename, char **out_uuid, - char **out_loaded_path) + char **out_loaded_path, + struct stat *out_st) { const char *uuid; - const char *tmp; - gsize len; + guint uuid_len; gs_free char *full_filename = NULL; gs_free char *ln = NULL; nm_assert (dirname && dirname[0] == '/'); nm_assert (filename && filename[0] && !strchr (filename, '/')); - if (filename[0] != '.') { - /* the hidden-uuid filename must start with '.'. That is, - * so that it does not conflict with regular keyfiles according - * to nm_keyfile_utils_ignore_filename(). */ - return FALSE; - } - - len = strlen (filename); - if ( len <= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED) - || memcmp (filename, NM_KEYFILE_PATH_PREFIX_NMLOADED, NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)) != 0) { - /* the filename does not have the right prefix. */ - return FALSE; - } - - tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; - len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); - - if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) - || memcmp (&tmp[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)], - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) != 0) { - /* the file does not have the right suffix. */ - return FALSE; - } - len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); - - if (!NM_IN_SET (len, 36, 40)) { - /* the remaining part of the filename has not the right length to - * contain a UUID (according to nm_utils_is_uuid()). */ - return FALSE; - } - - uuid = nm_strndup_a (100, tmp, len, NULL); - if (!nm_utils_is_uuid (uuid)) + uuid = nms_keyfile_loaded_uuid_is_filename (filename, &uuid_len); + if (!uuid) return FALSE; full_filename = g_build_filename (dirname, filename, NULL); if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED, full_filename, - NULL, + out_st, NULL)) return FALSE; @@ -124,7 +146,7 @@ nms_keyfile_loaded_uuid_read (const char *dirname, if (!ln) return FALSE; - NM_SET_OUT (out_uuid, g_strdup (uuid)); + NM_SET_OUT (out_uuid, g_strndup (uuid, uuid_len)); NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); return TRUE; @@ -149,7 +171,8 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, filename, NULL, out_uuid, - out_loaded_path)) + out_loaded_path, + NULL)) return FALSE; NM_SET_OUT (out_dirname, g_steal_pointer (&dirname)); diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h index 59b86d3142..b06ebcf15c 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -28,15 +28,25 @@ typedef enum { } NMSKeyfileFiletype; typedef enum { - /* the order here matters. Higher numbers are more important. E.g. /etc shadows - * connections from /usr/lib. */ - NMS_KEYFILE_STORAGE_TYPE_LIB, /* read-only, e.g. /usr/lib */ - NMS_KEYFILE_STORAGE_TYPE_ETC, /* read-write, persistent, e.g. /etc */ - NMS_KEYFILE_STORAGE_TYPE_RUN, /* read-write, runtime only, e.g. /var/run */ + NMS_KEYFILE_STORAGE_TYPE_RUN = 1, /* read-write, runtime only, e.g. /run */ + NMS_KEYFILE_STORAGE_TYPE_ETC = 2, /* read-write, persistent, e.g. /etc */ + NMS_KEYFILE_STORAGE_TYPE_LIB_BASE = 3, /* read-only, e.g. /usr/lib */ + + _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST = 1000, } NMSKeyfileStorageType; +static inline NMSKeyfileStorageType +NMS_KEYFILE_STORAGE_TYPE_LIB (guint run_idx) +{ + nm_assert (run_idx <= (_NMS_KEYFILE_STORAGE_TYPE_LIB_LAST - NMS_KEYFILE_STORAGE_TYPE_LIB_BASE)); + return NMS_KEYFILE_STORAGE_TYPE_LIB_BASE + run_idx; +} + /*****************************************************************************/ +const char *nms_keyfile_loaded_uuid_is_filename (const char *filename, + guint *out_uuid_len); + char *nms_keyfile_loaded_uuid_filename (const char *dirname, const char *uuid, gboolean temporary); @@ -45,7 +55,8 @@ gboolean nms_keyfile_loaded_uuid_read (const char *dirname, const char *filename, char **out_full_filename, char **out_uuid, - char **out_loaded_path); + char **out_loaded_path, + struct stat *out_st); gboolean nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, char **out_dirname, |