summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-07-01 18:28:48 +0200
committerThomas Haller <thaller@redhat.com>2019-07-08 14:23:15 +0200
commit852e0c5ee2ec9b2ac92401707a6ab8e2fd92c1d4 (patch)
tree91cc6b3cc83087153e8e2ca47b9a0eac3e2ce1ef
parente356fde636324f3fbb4ff3387dc6af1c9748517c (diff)
downloadNetworkManager-th/settings-delegate-storage-1.tar.gz
fixup! settings: rework tracking settings connections and settings pluginsth/settings-delegate-storage-1
-rw-r--r--Makefile.am4
-rw-r--r--src/settings/nm-settings-connection.c1
-rw-r--r--src/settings/nm-settings-plugin.c70
-rw-r--r--src/settings/nm-settings-plugin.h52
-rw-r--r--src/settings/nm-settings-storage.c73
-rw-r--r--src/settings/nm-settings-storage.h33
-rw-r--r--src/settings/nm-settings-utils.c245
-rw-r--r--src/settings/nm-settings-utils.h101
-rw-r--r--src/settings/nm-settings.c397
-rw-r--r--src/settings/nm-settings.h1
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c1073
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c122
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h91
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c1396
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.h10
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.c75
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.h103
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c103
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h23
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,