summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-06-13 17:12:20 +0200
committerThomas Haller <thaller@redhat.com>2019-07-08 13:57:19 +0200
commite356fde636324f3fbb4ff3387dc6af1c9748517c (patch)
tree06de379eff8473bf28060f5e066fb34263c35a79
parentf49f9dca6aecf808521fa926b7c7bdacc730dd39 (diff)
downloadNetworkManager-e356fde636324f3fbb4ff3387dc6af1c9748517c.tar.gz
settings: rework tracking settings connections and settings plugins
Completely rework how settings plugin handle connections and how NMSettings tracks the list of connections.. Previously, settings plugins would return objects of (a subtype of) type NMSettingsConnection. The NMSettingsConnection was tightly coupled with the settings plugin. That has a lot of downsides. Change that. When changing this basic relation how settings connections are tracked, everything falls appart. That's why this is a huge change. Also, since I have to largely rewrite the settings plugins, I also added support for multiple keyfile directories, handle in-memory connections only by keyfile plugin and use (partly) copy-on-write NMConnection instances. I don't want to spend effort rewriting large parts while preserving the old way, that I want anyway. E.g. while rewriting ifcfg-rh, I don't want to let it handle in-memory connections because that's not right long-term. -- If the settings plugins themself create subtypes of NMSettingsConnection instances, then a lot of knowledge about tracking connections moves to the plugins. Just try to follow the code what happend during nm_settings_add_connection(), it's astonishing how the logic is spread out: - nm_settings_add_connection() calls plugin's add_connection() - add_connection() creates a NMSettingsConnection subtype - the plugin has to know that it's called during add-connection and not emit NM_SETTINGS_PLUGIN_CONNECTION_ADDED signal - NMSettings calls claim_connection() which hocks up the new NMSettingsConnection instance and configures the instance (like calling nm_settings_connection_added()). It now sounds like not a lot, but try to follow that code. The logic is all over the place. Instead, settings plugins should have a very simple API for adding, modifying, deleting, loading and reloading connections. All the plugin does now is to return a NMSettingsStorage handle. The storage instance is a handle to identify a profile in storage (e.g. a particular file). The settings plugin is free to subtype NMSettingsStorage, but it's not necessary. There are no more events raised, and the settings plugin implements the small API in a straightforward manner. NMSettings now drives all of this. Even NMSettingsConnection has now very little concern about how it's tracked and delegates only to NMSettings. This should make settings plugins simpler. Currently settings plugins are so cumbersome to implement, that we avoid having them. It should not be like that and it should be easy, beneficial and lightweight to create a new settings plugin. The reason why keyfile and ifcfg-rh plugins are still so complicated is because the unique identifier of the profile is the UUID, but any number of files can provide the same id. While that is usually not the case, the code to handle these special cases makes them complicated. In particular reload and load is complicated due to the relation between the UUID and the files. -- NMSettingsConnection is exposed directly on D-Bus (being a subtype of NMDBusObject) but it was also a GObject type provided by the settings plugin. Hence, it was not possible to migrate a profile from one plugin to another. However that would be useful when one profile does not support a connection type (like ifcfg-rh not supporting VPN). Currently such migration is not implemented except for migrating them to/from keyfile's run directory. The problem is that migrating profiles in general is complicated but in some cases it is important to do. For example checkpoint rollback should recreate the profile in the same settings plugin, not just add it to persistent storage. This is not yet implemented. -- Previously, both keyfile and ifcfg-rh plugin implemented in-memory (unsaved) profiles, while ifupdown plugin cannot handle them. That meant duplication of code and a ifupdown profile could not be modified or made unsaved. This is now unified and only keyfile plugin handles in-memory profiles (bgo #744711). Also, NMSettings is aware of such profiles and treats them specially. In particular, NMSettings drives the migration between persistent and non-persistent storage. Note that other setting plugins theoretically may create truly generated, in-memory profiles. The settings plugin is free to generate and persist the profiles in any way the prefer. But the concept of "unsaved" profiles is now something explicitly handled by keyfile plugin. Also, these "unsaved" keyfile profiles are persisted to file system too to the /run directory. This is great for two reasons: first of all, all profiles from keyfile storage in fact have a backing file -- even the unsaved ones. It also means you can create "unsaved" profiles in /run and load them with `nmcli connection load`, meaning there is a file based API for creating unsaved profiles. The other advantage is that these profiles now survive restarting NetworkManager. It's paramount that restarting the daemon is as non-disruptive as possible. Persisting unsaved files to /run improves here significantly. -- In the past, NMSettingsConnection also implemented NMConnection interface. That was already changed a while ago and instead users call now nm_settings_connection_get_connection() to delegate to a NMSimpleConnection. What however still happend was that the NMConnection instance gets never swapped but instead the instance was modified with nm_connection_replace_settings_from_connection(), clear-secrets, etc. Change that and treat the NMConnection instance immutable. Instead of modifying it, reference/clone a new instance. This changes that previously when somebody wanted to keep a reference to an NMConnection, then the profile would be cloned. Now, it is supposed to be safe to reference the instance directly and everybody most ensure not to modify the instance. nmtst_connection_assert_unchanging() should help with that. The point is that the settings plugins keep references to the NMConnection instance, and so does the NMSettingsConnection. We want to avoid cloning the instances as long as they are the same. Likewise, the device's applied connection can now also be referenced instead of cloning it. This is not yet done, and possibly there are further improvements possible. -- Also implement multiple keyfile directores /usr/lib, /etc, /run (rh #1674545). It was always the case that multiple files could provide the same UUID (both in case of keyfile and ifcfg-rh). For keyfile plugin, if a profile in read-only storage in /usr/lib gets modified, then it gets actually stored in /etc (or /run, if the profile is unsaved). We also point to the currently loaded file via a symlink in /run. By linking to /dev/null, this means to hide the existing file. This is for example done when deleting a profile from immutable storage. Note that currently we only persist such symlinks to /run. If we'd come up with more complex policies, we could also persist them to /etc, but it's unclear how to do that exactly. The only problem is that these symlinks are not garbage collected and accumulate. So if you add/load/delete thousands of profiles, the symlinks pile up. A solution for that has to be found, but it's unclear how it should look exactly.
-rw-r--r--Makefile.am14
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec1
-rw-r--r--libnm-core/nm-connection.c3
-rw-r--r--libnm-core/nm-dbus-interface.h3
-rw-r--r--libnm-core/nm-keyfile-internal.h9
-rw-r--r--libnm-core/nm-keyfile-utils.c30
-rw-r--r--libnm-core/nm-keyfile.c6
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c14
-rw-r--r--src/devices/nm-device.c26
-rw-r--r--src/devices/wifi/nm-device-wifi.c7
-rw-r--r--src/devices/wifi/nm-iwd-manager.c15
-rw-r--r--src/meson.build3
-rw-r--r--src/nm-active-connection.c7
-rw-r--r--src/nm-checkpoint.c33
-rw-r--r--src/nm-manager.c114
-rw-r--r--src/nm-policy.c15
-rw-r--r--src/settings/nm-settings-connection.c1079
-rw-r--r--src/settings/nm-settings-connection.h222
-rw-r--r--src/settings/nm-settings-plugin.c202
-rw-r--r--src/settings/nm-settings-plugin.h171
-rw-r--r--src/settings/nm-settings-storage.c242
-rw-r--r--src/settings/nm-settings-storage.h117
-rw-r--r--src/settings/nm-settings.c1955
-rw-r--r--src/settings/nm-settings.h35
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c400
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h53
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c1745
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h24
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c17
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h8
-rw-r--r--src/settings/plugins/ifupdown/meson.build1
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-connection.c91
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-connection.h43
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c37
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.h13
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c217
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.h24
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c76
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c187
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.h43
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c1587
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.h22
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h5
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.c101
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.h118
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c27
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h16
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c38
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h6
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile-settings.c3
-rwxr-xr-xtools/meson-post-install.sh1
53 files changed, 6040 insertions, 3206 deletions
diff --git a/Makefile.am b/Makefile.am
index 85bdaefa27..29d485d06d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2092,13 +2092,15 @@ src_libNetworkManager_la_SOURCES = \
src/settings/nm-secret-agent.h \
src/settings/nm-settings-connection.c \
src/settings/nm-settings-connection.h \
+ src/settings/nm-settings-storage.c \
+ src/settings/nm-settings-storage.h \
src/settings/nm-settings-plugin.c \
src/settings/nm-settings-plugin.h \
src/settings/nm-settings.c \
src/settings/nm-settings.h \
\
- src/settings/plugins/keyfile/nms-keyfile-connection.c \
- src/settings/plugins/keyfile/nms-keyfile-connection.h \
+ src/settings/plugins/keyfile/nms-keyfile-storage.c \
+ src/settings/plugins/keyfile/nms-keyfile-storage.h \
src/settings/plugins/keyfile/nms-keyfile-plugin.c \
src/settings/plugins/keyfile/nms-keyfile-plugin.h \
src/settings/plugins/keyfile/nms-keyfile-reader.c \
@@ -2817,10 +2819,9 @@ $(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-connection.c \
- src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h \
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c \
- src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
+ src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h \
+ $(NULL)
src_settings_plugins_ifcfg_rh_libnm_settings_plugin_ifcfg_rh_la_CPPFLAGS = $(src_cppflags_base)
@@ -3133,8 +3134,6 @@ src_settings_plugins_ifupdown_libnms_ifupdown_core_la_LIBADD = \
$(NULL)
src_settings_plugins_ifupdown_libnm_settings_plugin_ifupdown_la_SOURCES = \
- src/settings/plugins/ifupdown/nms-ifupdown-connection.c \
- src/settings/plugins/ifupdown/nms-ifupdown-connection.h \
src/settings/plugins/ifupdown/nms-ifupdown-plugin.c \
src/settings/plugins/ifupdown/nms-ifupdown-plugin.h \
$(NULL)
@@ -4832,6 +4831,7 @@ install-data-hook: $(install_data_hook)
$(mkinstalldirs) -m 0755 $(DESTDIR)$(nmconfdir)/dnsmasq-shared.d
$(mkinstalldirs) -m 0755 $(DESTDIR)$(nmlibdir)/conf.d
$(mkinstalldirs) -m 0755 $(DESTDIR)$(nmlibdir)/VPN
+ $(mkinstalldirs) -m 0755 $(DESTDIR)$(nmlibdir)/system-connections
$(mkinstalldirs) -m 0700 $(DESTDIR)$(nmstatedir)
$(mkinstalldirs) -m 0755 $(DESTDIR)$(plugindir)
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index b775f1dfa9..b7e721f864 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -843,6 +843,7 @@ fi
%dir %{nmlibdir}
%dir %{nmlibdir}/conf.d
%dir %{nmlibdir}/VPN
+%dir %{nmlibdir}/system-connections
%{_mandir}/man1/*
%{_mandir}/man5/*
%{_mandir}/man7/nmcli-examples.7*
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 615faae7d1..2532b64a35 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -1811,6 +1811,9 @@ const char _nmtst_connection_unchanging_user_data = 0;
void
nmtst_connection_assert_unchanging (NMConnection *connection)
{
+ if (!connection)
+ return;
+
nm_assert (NM_IS_CONNECTION (connection));
if (g_signal_handler_find (connection,
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 9950d67bfc..c68c7757b2 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -1014,6 +1014,9 @@ typedef enum { /*< flags >*/
* is not deleted but merely the connection is decoupled from the file
* on disk. If you later delete an in-memory connection, the connection
* on disk will be deleted as well.
+ * Note: with 1.20, this flag is no longer implemented because in-memory connections
+ * are also persisted under /run. For the moment, this behaves the same as
+ * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED.
* @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED: this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY,
* but if the connection has a corresponding file on disk, the association between
* the connection and the file is forgotten but the file is not modified.
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index a73f85bbd1..c50eb28825 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -162,6 +162,8 @@ GKeyFile *nm_keyfile_write (NMConnection *connection,
char *nm_keyfile_plugin_kf_get_string (GKeyFile *kf, const char *group, const char *key, GError **error);
void nm_keyfile_plugin_kf_set_string (GKeyFile *kf, const char *group, const char *key, const char *value);
+int nm_key_file_get_boolean (GKeyFile *kf, const char *group, const char *key, int default_value);
+
void _nm_keyfile_copy (GKeyFile *dst, GKeyFile *src);
gboolean _nm_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b);
gboolean _nm_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order);
@@ -169,8 +171,13 @@ gboolean _nm_keyfile_has_values (GKeyFile *keyfile);
/*****************************************************************************/
+#define NM_KEYFILE_GROUP_NMMETA ".nmmeta"
+#define NM_KEYFILE_KEY_NMMETA_NM_GENERATED "nm-generated"
+#define NM_KEYFILE_KEY_NMMETA_VOLATILE "volatile"
+
+#define NM_KEYFILE_PATH_NAME_LIB NMLIBDIR "/system-connections"
#define NM_KEYFILE_PATH_NAME_ETC_DEFAULT NMCONFDIR "/system-connections"
-#define NM_KEYFILE_PATH_NAME_RUN NMRUNDIR "/system-connections"
+#define NM_KEYFILE_PATH_NAME_RUN NMRUNDIR "/system-connections"
#define NM_KEYFILE_PATH_SUFFIX_NMCONNECTION ".nmconnection"
diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c
index 71e6ed9ca0..1a3d591c74 100644
--- a/libnm-core/nm-keyfile-utils.c
+++ b/libnm-core/nm-keyfile-utils.c
@@ -28,6 +28,36 @@
#include "nm-setting-wireless.h"
#include "nm-setting-wireless-security.h"
+/*****************************************************************************/
+
+/**
+ * nm_key_file_get_boolean:
+ * @kf: the #GKeyFile
+ * @group: the group
+ * @key: the key
+ * @default_value: the default value if the value is set or not parsable as a boolean.
+ *
+ * Replacement for g_key_file_get_boolean() uses g_key_file_parse_value_as_boolean(),
+ * which is very odd as it only considers the first word. This uses _nm_utils_ascii_str_to_bool()
+ * what we use everywhere.
+ *
+ * Returns: either %TRUE or %FALSE if the key exists and is parsable as a boolean.
+ * Otherwise, @default_value.
+ */
+int
+nm_key_file_get_boolean (GKeyFile *kf, const char *group, const char *key, int default_value)
+{
+ gs_free char *value = NULL;
+
+ value = g_key_file_get_value (kf, group, key, NULL);
+
+ if (!value)
+ return default_value;
+ return _nm_utils_ascii_str_to_bool (value, default_value);
+}
+
+/*****************************************************************************/
+
typedef struct {
const char *setting;
const char *alias;
diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c
index 8add4bfccf..83b92006d7 100644
--- a/libnm-core/nm-keyfile.c
+++ b/libnm-core/nm-keyfile.c
@@ -3537,7 +3537,9 @@ nm_keyfile_read (GKeyFile *keyfile,
vpn_secrets = TRUE;
} else if (NM_STR_HAS_PREFIX (groups[i], NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER))
_read_setting_wireguard_peer (&info);
- else
+ else if (nm_streq (groups[i], NM_KEYFILE_GROUP_NMMETA)) {
+ /* pass */
+ } else
_read_setting (&info);
info.group = NULL;
@@ -3981,7 +3983,7 @@ nm_keyfile_utils_ignore_filename (const char *filename, gboolean require_extensi
if (require_extension) {
if ( l <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)
- || !g_str_has_suffix (base, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION))
+ || !NM_STR_HAS_SUFFIX (base, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION))
return TRUE;
return FALSE;
}
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 5360992282..e8ca6dd159 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -235,15 +235,17 @@ pan_connection_check_create (NMBluezDevice *self)
* which then already finds the suitable connection in priv->connections. This is confusing,
* so block the signal. check_emit_usable will succeed after this function call returns. */
g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self);
- added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
+ nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error);
g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
if (added) {
nm_assert (!g_slist_find (priv->connections, added));
nm_assert (connection_compatible (self, added));
-
- nm_settings_connection_set_flags (added, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE);
-
priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
priv->pan_connection = added;
nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
@@ -392,7 +394,7 @@ cp_connection_removed (NMSettings *settings,
static void
cp_connection_updated (NMSettings *settings,
NMSettingsConnection *sett_conn,
- gboolean by_user,
+ guint update_reason_u,
NMBluezDevice *self)
{
if (_internal_track_connection (self, sett_conn,
@@ -1225,7 +1227,7 @@ dispose (GObject *object)
if (to_delete) {
nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
nm_settings_connection_get_id (to_delete), nm_settings_connection_get_uuid (to_delete));
- nm_settings_connection_delete (to_delete, NULL);
+ nm_settings_connection_delete (to_delete, FALSE);
g_object_unref (to_delete);
}
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 6a27469d41..5f7027d82a 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -12399,16 +12399,28 @@ nm_device_set_ip_config (NMDevice *self,
if ( nm_device_sys_iface_state_is_external (self)
&& (settings_connection = nm_device_get_settings_connection (self))
- && NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
+ && NM_FLAGS_ALL (nm_settings_connection_get_flags (settings_connection),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
&& nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request.obj)) == NM_ACTIVATION_TYPE_EXTERNAL) {
- g_object_freeze_notify (G_OBJECT (settings_connection));
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_add_setting (nm_settings_connection_get_connection (settings_connection),
+ gs_unref_object NMConnection *new_connection = NULL;
+
+ new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (settings_connection));
+
+ nm_connection_add_setting (new_connection,
IS_IPv4
? nm_ip4_config_create_setting (priv->ip_config_4)
: nm_ip6_config_create_setting (priv->ip_config_6));
- g_object_thaw_notify (G_OBJECT (settings_connection));
+
+ nm_settings_connection_update (settings_connection,
+ new_connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
+ "update-external",
+ NULL);
}
nm_device_queue_recheck_assume (self);
@@ -14360,7 +14372,7 @@ cp_connection_added (NMSettings *settings, NMSettingsConnection *sett_conn, gpoi
}
static void
-cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, gboolean by_user, gpointer user_data)
+cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, guint update_reason_u, gpointer user_data)
{
cp_connection_added_or_updated (user_data, sett_conn);
}
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index aff631f889..a1fa96be9f 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -1760,9 +1760,10 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
}
if (secrets) {
if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req),
- nm_act_request_get_applied_connection (req),
- NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
- secrets, &error)) {
+ nm_act_request_get_applied_connection (req),
+ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+ secrets,
+ &error)) {
wifi_secrets_cancel (self);
nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
} else {
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index a2057a51d6..8889bb204a 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -469,19 +469,18 @@ mirror_8021x_connection (NMIwdManager *self,
if (!nm_connection_normalize (connection, NULL, NULL, NULL))
return NULL;
- settings_connection = nm_settings_add_connection (priv->settings, connection,
- FALSE, &error);
- if (!settings_connection) {
+ if (!nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &settings_connection,
+ &error)) {
_LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s",
name, error->message);
g_error_free (error);
return NULL;
}
- nm_settings_connection_set_flags (settings_connection,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
- NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
- TRUE);
return settings_connection;
}
@@ -498,7 +497,7 @@ mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn)
/* If connection has not been saved since we created it
* in interface_added it too can be removed now. */
if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
- nm_settings_connection_delete (sett_conn, NULL);
+ nm_settings_connection_delete (sett_conn, FALSE);
g_object_unref (sett_conn);
}
diff --git a/src/meson.build b/src/meson.build
index aec6eceb44..698e05acd1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -103,7 +103,7 @@ sources = files(
'dnsmasq/nm-dnsmasq-manager.c',
'dnsmasq/nm-dnsmasq-utils.c',
'ppp/nm-ppp-manager-call.c',
- 'settings/plugins/keyfile/nms-keyfile-connection.c',
+ 'settings/plugins/keyfile/nms-keyfile-storage.c',
'settings/plugins/keyfile/nms-keyfile-plugin.c',
'settings/plugins/keyfile/nms-keyfile-reader.c',
'settings/plugins/keyfile/nms-keyfile-utils.c',
@@ -113,6 +113,7 @@ sources = files(
'settings/nm-settings.c',
'settings/nm-settings-connection.c',
'settings/nm-settings-plugin.c',
+ 'settings/nm-settings-storage.c',
'supplicant/nm-supplicant-config.c',
'supplicant/nm-supplicant-interface.c',
'supplicant/nm-supplicant-manager.c',
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index 06932b5f15..b937f7329e 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -181,7 +181,7 @@ NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags
static void
_settings_connection_updated (NMSettingsConnection *sett_conn,
- gboolean by_user,
+ guint update_reason_u,
gpointer user_data)
{
NMActiveConnection *self = user_data;
@@ -522,8 +522,9 @@ nm_active_connection_clear_secrets (NMActiveConnection *self)
if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj,
priv->applied_connection,
NM_SETTING_COMPARE_FLAG_NONE)) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (priv->settings_connection.obj));
+ nm_settings_connection_clear_secrets (priv->settings_connection.obj,
+ FALSE,
+ FALSE);
}
nm_connection_clear_secrets (priv->applied_connection);
}
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 000edee5b7..b8277539e3 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -217,19 +217,35 @@ restore_and_activate_connection (NMCheckpoint *self,
gs_unref_object NMAuthSubject *subject = NULL;
GError *local_error = NULL;
gboolean need_update, need_activation;
+ NMSettingsConnectionPersistMode persist_mode;
+ NMSettingsConnectionIntFlags sett_flags;
+ NMSettingsConnectionIntFlags sett_mask;
connection = find_settings_connection (self,
dev_checkpoint,
&need_update,
&need_activation);
+
+ /* FIXME(settings-rework): we need to ensure that we re-create/update the profile
+ * for the same settings plugin. E.g. if it was a keyfile in /run or /etc,
+ * it must be again. If it was previously handled by a certain settings plugin,
+ * so it must again.
+ *
+ * FIXME: set the right storage flags for the setting (volatile, nm-generated). */
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
+ sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+ sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+
if (connection) {
if (need_update) {
_LOGD ("rollback: updating connection %s",
nm_settings_connection_get_uuid (connection));
nm_settings_connection_update (connection,
dev_checkpoint->settings_connection,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+ persist_mode,
+ sett_flags,
+ sett_mask,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
"checkpoint-rollback",
NULL);
}
@@ -238,11 +254,12 @@ restore_and_activate_connection (NMCheckpoint *self,
_LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
- connection = nm_settings_add_connection (NM_SETTINGS_GET,
- dev_checkpoint->settings_connection,
- TRUE,
- &local_error);
- if (!connection) {
+ if (!nm_settings_add_connection (NM_SETTINGS_GET,
+ dev_checkpoint->settings_connection,
+ persist_mode,
+ sett_flags,
+ &connection,
+ &local_error)) {
_LOGD ("rollback: connection add failure: %s", local_error->message);
g_clear_error (&local_error);
return FALSE;
@@ -428,7 +445,7 @@ next_dev:
nm_settings_connection_get_uuid (con))) {
_LOGD ("rollback: deleting new connection %s",
nm_settings_connection_get_uuid (con));
- nm_settings_connection_delete (con, NULL);
+ nm_settings_connection_delete (con, FALSE);
}
}
}
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 52433049fa..605e04e238 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -94,7 +94,8 @@ typedef struct {
struct {
GDBusMethodInvocation *invocation;
NMConnection *connection;
- NMSettingsConnectionPersistMode persist;
+ NMSettingsConnectionPersistMode persist_mode;
+ bool is_volatile:1;
} add_and_activate;
};
} ac_auth;
@@ -378,7 +379,8 @@ static void _add_and_activate_auth_done (NMManager *self,
NMActiveConnection *active,
NMConnection *connection,
GDBusMethodInvocation *invocation,
- NMSettingsConnectionPersistMode persist,
+ NMSettingsConnectionPersistMode persist_mode,
+ gboolean is_volatile,
gboolean success,
const char *error_desc);
static void _activation_auth_done (NMManager *self,
@@ -495,7 +497,8 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *self,
NMActiveConnection *active_take,
GDBusMethodInvocation *invocation_take,
NMConnection *connection_take,
- NMSettingsConnectionPersistMode persist)
+ NMSettingsConnectionPersistMode persist_mode,
+ gboolean is_volatile)
{
AsyncOpData *async_op_data;
@@ -508,7 +511,8 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *self,
async_op_data->ac_auth.active = active_take;
async_op_data->ac_auth.add_and_activate.invocation = invocation_take;
async_op_data->ac_auth.add_and_activate.connection = connection_take;
- async_op_data->ac_auth.add_and_activate.persist = persist;
+ async_op_data->ac_auth.add_and_activate.persist_mode = persist_mode;
+ async_op_data->ac_auth.add_and_activate.is_volatile = is_volatile;
c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst);
return async_op_data;
}
@@ -550,7 +554,8 @@ _async_op_complete_ac_auth_cb (NMActiveConnection *active,
async_op_data->ac_auth.active,
async_op_data->ac_auth.add_and_activate.connection,
async_op_data->ac_auth.add_and_activate.invocation,
- async_op_data->ac_auth.add_and_activate.persist,
+ async_op_data->ac_auth.add_and_activate.persist_mode,
+ async_op_data->ac_auth.add_and_activate.is_volatile,
success,
error_desc);
g_object_unref (async_op_data->ac_auth.add_and_activate.connection);
@@ -831,7 +836,7 @@ _delete_volatile_connection_do (NMManager *self,
_LOGD (LOGD_DEVICE, "volatile connection disconnected. Deleting connection '%s' (%s)",
nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
- nm_settings_connection_delete (connection, NULL);
+ nm_settings_connection_delete (connection, FALSE);
}
/* Returns: whether to notify D-Bus of the removal or not */
@@ -2140,7 +2145,7 @@ connection_added_cb (NMSettings *settings,
static void
connection_updated_cb (NMSettings *settings,
NMSettingsConnection *sett_conn,
- gboolean by_user,
+ guint update_reason_u,
NMManager *self)
{
connection_changed_on_idle (self, sett_conn);
@@ -2660,8 +2665,13 @@ get_existing_connection (NMManager *self,
nm_device_assume_state_reset (device);
- added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
- if (!added) {
+ if (!nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error)) {
_LOG2W (LOGD_SETTINGS, device, "assume: failure to save generated connection '%s': %s",
nm_connection_get_id (connection),
error->message);
@@ -2669,10 +2679,6 @@ get_existing_connection (NMManager *self,
return NULL;
}
- nm_settings_connection_set_flags (added,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
- NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
- TRUE);
NM_SET_OUT (out_generated, TRUE);
return added;
}
@@ -2774,7 +2780,7 @@ recheck_assume_connection (NMManager *self,
if (generated) {
_LOG2D (LOGD_DEVICE, device, "assume: deleting generated connection after assuming failed");
- nm_settings_connection_delete (sett_conn, NULL);
+ nm_settings_connection_delete (sett_conn, FALSE);
} else {
if (nm_device_sys_iface_state_get (device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME)
nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
@@ -5315,14 +5321,11 @@ activation_add_done (NMSettings *settings,
NMManager *self;
gs_unref_object NMActiveConnection *active = NULL;
gs_free_error GError *local = NULL;
- gpointer persist_ptr;
- NMSettingsConnectionPersistMode persist;
gpointer async_op_type_ptr;
AsyncOpType async_op_type;
GVariant *result_floating;
- nm_utils_user_data_unpack (user_data, &self, &active, &persist_ptr, &async_op_type_ptr);
- persist = GPOINTER_TO_INT (persist_ptr);
+ nm_utils_user_data_unpack (user_data, &self, &active, &async_op_type_ptr);
async_op_type = GPOINTER_TO_INT (async_op_type_ptr);
if (error)
@@ -5330,17 +5333,8 @@ activation_add_done (NMSettings *settings,
nm_active_connection_set_settings_connection (active, new_connection);
- if (!_internal_activate_generic (self, active, &local)) {
- error = local;
+ if (!_internal_activate_generic (self, active, &local))
goto fail;
- }
-
- nm_settings_connection_update (new_connection,
- NULL,
- persist,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED,
- "add-and-activate",
- NULL);
if (async_op_type == ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE) {
result_floating = g_variant_new ("(oo)",
@@ -5363,13 +5357,17 @@ activation_add_done (NMSettings *settings,
return;
fail:
- nm_assert (error);
+ if (local) {
+ nm_assert (!error);
+ error = local;
+ } else
+ nm_assert (error);
nm_active_connection_set_state_fail (active,
NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
error->message);
if (new_connection)
- nm_settings_connection_delete (new_connection, NULL);
+ nm_settings_connection_delete (new_connection, FALSE);
g_dbus_method_invocation_return_gerror (context, error);
nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
NULL,
@@ -5385,7 +5383,8 @@ _add_and_activate_auth_done (NMManager *self,
NMActiveConnection *active,
NMConnection *connection,
GDBusMethodInvocation *invocation,
- NMSettingsConnectionPersistMode persist,
+ NMSettingsConnectionPersistMode persist_mode,
+ gboolean is_volatile,
gboolean success,
const char *error_desc)
{
@@ -5413,13 +5412,15 @@ _add_and_activate_auth_done (NMManager *self,
* shutdown. */
nm_settings_add_connection_dbus (priv->settings,
connection,
- FALSE,
+ persist_mode,
+ ( is_volatile
+ ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE),
nm_active_connection_get_subject (active),
invocation,
activation_add_done,
nm_utils_user_data_pack (self,
g_object_ref (active),
- GINT_TO_POINTER (persist),
GINT_TO_POINTER (async_op_type)));
}
@@ -5445,7 +5446,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
const char *device_path;
const char *specific_object_path;
gs_free NMConnection **conns = NULL;
- NMSettingsConnectionPersistMode persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ NMSettingsConnectionPersistMode persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ gboolean is_volatile = FALSE;
gboolean bind_dbus_client = FALSE;
AsyncOpType async_op_type;
@@ -5474,13 +5476,17 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
&& g_variant_is_of_type (option_value, G_VARIANT_TYPE_STRING)) {
s = g_variant_get_string (option_value, NULL);
- if (nm_streq (s, "volatile"))
- persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY;
- else if (nm_streq (s, "memory"))
- persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
- else if (nm_streq (s, "disk"))
- persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
- else {
+ is_volatile = FALSE;
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+
+ if (nm_streq (s, "volatile")) {
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ is_volatile = TRUE;
+ } else if (nm_streq (s, "memory"))
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ else if (nm_streq (s, "disk")) {
+ /* pass */
+ } else {
error = g_error_new_literal (NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"Option \"persist\" must be one of \"volatile\", \"memory\" or \"disk\"");
@@ -5599,7 +5605,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
active,
invocation,
incompl_conn,
- persist));
+ persist_mode,
+ is_volatile));
/* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */
g_steal_pointer (&incompl_conn);
@@ -6549,8 +6556,9 @@ nm_manager_start (NMManager *self, GError **error)
gs_free NMSettingsConnection **connections = NULL;
guint i;
- if (!nm_settings_start (priv->settings, error))
- return FALSE;
+ nm_device_factory_manager_load_factories (_register_device_factory, self);
+
+ nm_device_factory_manager_for_each_factory (start_factory, NULL);
/* Set initial radio enabled/disabled state */
for (i = 0; i < RFKILL_TYPE_MAX; i++) {
@@ -6573,16 +6581,15 @@ nm_manager_start (NMManager *self, GError **error)
manager_update_radio_enabled (self, rstate, enabled);
}
- /* Log overall networking status - enabled/disabled */
_LOGI (LOGD_CORE, "Networking is %s by state file",
priv->net_enabled ? "enabled" : "disabled");
system_unmanaged_devices_changed_cb (priv->settings, NULL, self);
+
hostname_changed_cb (priv->hostname_manager, NULL, self);
- /* Start device factories */
- nm_device_factory_manager_load_factories (_register_device_factory, self);
- nm_device_factory_manager_for_each_factory (start_factory, NULL);
+ if (!nm_settings_start (priv->settings, error))
+ return FALSE;
nm_platform_process_events (priv->platform);
@@ -6596,10 +6603,11 @@ nm_manager_start (NMManager *self, GError **error)
/* Load VPN plugins */
priv->vpn_manager = g_object_ref (nm_vpn_manager_get ());
- /* Connections added before the manager is started do not emit
- * connection-added signals thus devices have to be created manually.
- */
_LOGD (LOGD_CORE, "creating virtual devices...");
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
+ G_CALLBACK (connection_added_cb), self);
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
+ G_CALLBACK (connection_updated_cb), self);
connections = nm_settings_get_connections_clone (priv->settings, NULL,
NULL, NULL,
nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
@@ -7334,10 +7342,6 @@ constructed (GObject *object)
G_CALLBACK (settings_startup_complete_changed), self);
g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS,
G_CALLBACK (system_unmanaged_devices_changed_cb), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
- G_CALLBACK (connection_added_cb), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
- G_CALLBACK (connection_updated_cb), self);
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self);
priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 9d5b027c28..04c855ef53 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -1846,8 +1846,7 @@ device_state_changed (NMDevice *device,
}
}
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (sett_conn));
+ nm_settings_connection_clear_secrets (sett_conn, FALSE, FALSE);
}
break;
case NM_DEVICE_STATE_ACTIVATED:
@@ -1858,9 +1857,7 @@ device_state_changed (NMDevice *device,
/* And clear secrets so they will always be requested from the
* settings service when the next connection is made.
*/
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (sett_conn));
+ nm_settings_connection_clear_secrets (sett_conn, FALSE, FALSE);
}
/* Add device's new IPv4 and IPv6 configs to DNS */
@@ -2399,16 +2396,17 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
static void
connection_updated (NMSettings *settings,
NMSettingsConnection *connection,
- gboolean by_user,
+ guint update_reason_u,
gpointer user_data)
{
NMPolicyPrivate *priv = user_data;
NMPolicy *self = _PRIV_TO_SELF (priv);
+ NMSettingsConnectionUpdateReason update_reason = update_reason_u;
const CList *tmp_lst;
NMDevice *device = NULL;
NMDevice *dev;
- if (by_user) {
+ if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)) {
/* find device with given connection */
nm_manager_for_each_device (priv->manager, dev, tmp_lst) {
if (nm_device_get_settings_connection (dev) == connection) {
@@ -2419,9 +2417,6 @@ connection_updated (NMSettings *settings,
if (device)
nm_device_reapply_settings_immediately (device);
-
- /* Reset auto retries back to default since connection was updated */
- nm_settings_connection_autoconnect_retries_reset (connection);
}
schedule_activate_all (self);
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 6368b3d5a0..937b9b5eb1 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -37,6 +37,8 @@
#include "NetworkManagerUtils.h"
#include "nm-core-internal.h"
#include "nm-audit-manager.h"
+#include "nm-settings.h"
+#include "settings/plugins/keyfile/nms-keyfile-storage.h"
#define AUTOCONNECT_RETRIES_UNSET -2
#define AUTOCONNECT_RETRIES_FOREVER -1
@@ -67,13 +69,11 @@ nm_settings_connections_array_to_connections (NMSettingsConnection *const*connec
NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection,
PROP_UNSAVED,
- PROP_READY,
PROP_FLAGS,
PROP_FILENAME,
);
enum {
- REMOVED,
UPDATED_INTERNAL,
FLAGS_CHANGED,
LAST_SIGNAL
@@ -83,21 +83,12 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct _NMSettingsConnectionPrivate {
+ NMSettings *settings;
+
NMKeyFileDB *kf_db_timestamps;
NMKeyFileDB *kf_db_seen_bssids;
NMAgentManager *agent_mgr;
- NMSessionMonitor *session_monitor;
- gulong session_changed_id;
-
- NMSettingsConnectionIntFlags flags:5;
-
- bool removed:1;
- bool ready:1;
-
- bool timestamp_set:1;
-
- NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
/* List of pending authentication requests */
CList auth_lst_head;
@@ -106,6 +97,12 @@ typedef struct _NMSettingsConnectionPrivate {
NMConnection *connection;
+ NMSettingsStorage *storage;
+
+ char *filename;
+
+ NMDevice *default_wired_device;
+
/* Caches secrets from on-disk connections; were they not cached any
* call to nm_connection_clear_secrets() wipes them out and we'd have
* to re-read them from disk which defeats the purpose of having the
@@ -121,8 +118,6 @@ typedef struct _NMSettingsConnectionPrivate {
*/
GVariant *agent_secrets;
- char *filename;
-
GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
guint64 timestamp; /* Up-to-date timestamp of connection use */
@@ -130,10 +125,21 @@ typedef struct _NMSettingsConnectionPrivate {
guint64 last_secret_agent_version_id;
int autoconnect_retries;
+
gint32 autoconnect_retries_blocked_until;
+ bool timestamp_set:1;
+
+ NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
+
+ NMSettingsConnectionIntFlags flags:5;
+
} NMSettingsConnectionPrivate;
+struct _NMSettingsConnectionClass {
+ NMDBusObjectClass parent;
+};
+
G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT)
#define NM_SETTINGS_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMSettingsConnection, NM_IS_SETTINGS_CONNECTION)
@@ -167,6 +173,68 @@ static const GDBusSignalInfo signal_info_updated;
static const GDBusSignalInfo signal_info_removed;
static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
+static void update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new);
+static void update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new);
+
+/*****************************************************************************/
+
+NMDevice *
+nm_settings_connection_default_wired_get_device (NMSettingsConnection *self)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+ nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device));
+
+ return priv->default_wired_device;
+}
+
+void
+nm_settings_connection_default_wired_set_device (NMSettingsConnection *self,
+ NMDevice *device)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+ nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device));
+ nm_assert (!device || NM_IS_DEVICE (device));
+
+ nm_assert ((!!priv->default_wired_device) != (!!device));
+
+ priv->default_wired_device = device;
+}
+
+/*****************************************************************************/
+
+NMSettingsStorage *
+nm_settings_connection_get_storage (NMSettingsConnection *self)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+ return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->storage;
+}
+
+void
+_nm_settings_connection_set_storage (NMSettingsConnection *self,
+ NMSettingsStorage *storage)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ const char *filename;
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+ nm_assert ( !priv->storage
+ || nm_streq (nm_settings_storage_get_uuid (storage),
+ nm_settings_storage_get_uuid (priv->storage)));
+
+ nm_g_object_ref_set (&priv->storage, storage);
+
+ filename = nm_settings_storage_get_filename (priv->storage);
+
+ if (!nm_streq0 (priv->filename, filename)) {
+ g_free (priv->filename);
+ priv->filename = g_strdup (filename);
+ _notify (self, PROP_FILENAME);
+ }
+}
+
/*****************************************************************************/
static GHashTable *
@@ -185,6 +253,50 @@ nm_settings_connection_get_connection (NMSettingsConnection *self)
return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->connection;
}
+void
+_nm_settings_connection_set_connection (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMConnection **out_connection_old,
+ NMSettingsConnectionUpdateReason update_reason)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection_old = NULL;
+
+ nm_assert (NM_IS_CONNECTION (new_connection));
+ nm_assert (NM_IS_SETTINGS_STORAGE (priv->storage));
+ nm_assert (nm_streq0 (nm_settings_storage_get_uuid (priv->storage), nm_connection_get_uuid (new_connection)));
+ nm_assert (!out_connection_old || !*out_connection_old);
+
+ if ( !priv->connection
+ || !nm_connection_compare (priv->connection,
+ new_connection,
+ NM_SETTING_COMPARE_FLAG_EXACT)) {
+ connection_old = priv->connection;
+ priv->connection = g_object_ref (new_connection);
+ nmtst_connection_assert_unchanging (priv->connection);
+
+ /* note that we only return @connection_old if the new connection actually differs from
+ * before.
+ *
+ * So, there are three cases:
+ *
+ * - return %NULL when setting the connection the first time.
+ * - return %NULL if setting a profile with the same content that we already have.
+ * - return the previous pointer if the connection changed. */
+ NM_SET_OUT (out_connection_old, g_steal_pointer (&connection_old));
+ }
+
+ if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS))
+ update_system_secrets_cache (self, NULL);
+ else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS))
+ update_system_secrets_cache (self, priv->connection);
+
+ if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS))
+ update_agent_secrets_cache (self, NULL);
+ else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS))
+ update_agent_secrets_cache (self, priv->connection);
+}
+
/*****************************************************************************/
gboolean
@@ -214,34 +326,23 @@ nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *s
/*****************************************************************************/
-static void
-set_visible (NMSettingsConnection *self, gboolean new_visible)
-{
- nm_settings_connection_set_flags (self,
- NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,
- new_visible);
-}
-
-void
-nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
+gboolean
+nm_settings_connection_check_visibility (NMSettingsConnection *self,
+ NMSessionMonitor *session_monitor)
{
- NMSettingsConnectionPrivate *priv;
NMSettingConnection *s_con;
guint32 num, i;
- g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
- priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ nm_assert (NM_IS_SESSION_MONITOR (session_monitor));
s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
/* Check every user in the ACL for a session */
num = nm_setting_connection_get_num_permissions (s_con);
- if (num == 0) {
- /* Visible to all */
- set_visible (self, TRUE);
- return;
- }
+ if (num == 0)
+ return TRUE;
for (i = 0; i < num; i++) {
const char *user;
@@ -251,20 +352,13 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
continue;
if (!nm_session_monitor_user_to_uid (user, &uid))
continue;
- if (!nm_session_monitor_session_exists (priv->session_monitor, uid, FALSE))
+ if (!nm_session_monitor_session_exists (session_monitor, uid, FALSE))
continue;
- set_visible (self, TRUE);
- return;
+ return TRUE;
}
- set_visible (self, FALSE);
-}
-
-static void
-session_changed_cb (NMSessionMonitor *self, NMSettingsConnection *sett_conn)
-{
- nm_settings_connection_recheck_visibility (sett_conn);
+ return FALSE;
}
/*****************************************************************************/
@@ -310,7 +404,8 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser);
- if (agent && nm_secret_agent_has_permission (agent, permission))
+ if ( agent
+ && nm_secret_agent_has_permission (agent, permission))
return TRUE;
}
}
@@ -321,20 +416,37 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
/*****************************************************************************/
static void
-update_system_secrets_cache (NMSettingsConnection *self)
+update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
gs_unref_object NMConnection *connection_cloned = NULL;
+ gs_unref_variant GVariant *old_secrets = NULL;
- nm_clear_pointer (&priv->system_secrets, g_variant_unref);
+ old_secrets = g_steal_pointer (&priv->system_secrets);
- connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
+ if (!new)
+ goto out;
+
+ /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
+ * in particular if there are no secrets to begin with. */
+
+ connection_cloned = nm_simple_connection_new_clone (new);
/* Clear out non-system-owned and not-saved secrets */
_nm_connection_clear_secrets_by_secret_flags (connection_cloned,
NM_SETTING_SECRET_FLAG_NONE);
priv->system_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
+
+out:
+ if (_LOGT_ENABLED ()) {
+ if ((!!old_secrets) != (!!priv->system_secrets)) {
+ _LOGT ("update system secrets: secrets %s",
+ old_secrets ? "cleared" : "set");
+ } else if ( priv->system_secrets
+ && !g_variant_equal (old_secrets, priv->system_secrets))
+ _LOGT ("update system secrets: secrets updated");
+ }
}
static void
@@ -342,11 +454,17 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
gs_unref_object NMConnection *connection_cloned = NULL;
+ gs_unref_variant GVariant *old_secrets = NULL;
- nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
+ old_secrets = g_steal_pointer (&priv->agent_secrets);
- connection_cloned = nm_simple_connection_new_clone ( new
- ?: nm_settings_connection_get_connection (self));
+ if (!new)
+ goto out;
+
+ /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
+ * in particular if there are no secrets to begin with. */
+
+ connection_cloned = nm_simple_connection_new_clone (new);
/* Clear out non-system-owned secrets */
_nm_connection_clear_secrets_by_secret_flags (connection_cloned,
@@ -354,328 +472,137 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
| NM_SETTING_SECRET_FLAG_AGENT_OWNED);
priv->agent_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
-}
-
-static void
-secrets_cleared_cb (NMConnection *connection, NMSettingsConnection *self)
-{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- /* Clear agent secrets when connection's secrets are cleared since agent
- * secrets are transient.
- */
- nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
-}
-
-static void
-set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode)
-{
- NMSettingsConnectionIntFlags flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
- const NMSettingsConnectionIntFlags ALL = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
- | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
- | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
- switch (persist_mode) {
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK:
- flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
- break;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY:
- flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
- break;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY:
- flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED |
- NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
- break;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED:
- /* only set the connection as unsaved, but preserve the nm-generated
- * and volatile flag. */
- nm_settings_connection_set_flags (self,
- NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
- TRUE);
- return;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED:
- /* Nothing to do */
- return;
+out:
+ if (_LOGT_ENABLED ()) {
+ if ((!!old_secrets) != (!!priv->agent_secrets)) {
+ _LOGT ("update agent secrets: secrets %s",
+ old_secrets ? "cleared" : "set");
+ } else if ( priv->agent_secrets
+ && !g_variant_equal (old_secrets, priv->agent_secrets))
+ _LOGT ("update agent secrets: secrets updated");
}
-
- nm_settings_connection_set_flags_full (self, ALL, flags);
}
-static void
-_emit_updated (NMSettingsConnection *self, gboolean by_user)
+void
+nm_settings_connection_clear_secrets (NMSettingsConnection *self,
+ gboolean clear_cached_system_secrets,
+ gboolean persist)
{
- nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
- &interface_info_settings_connection,
- &signal_info_updated,
- "()");
- g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user);
-}
+ gs_unref_object NMConnection *connection_cloned = NULL;
-static void
-connection_changed_cb (NMConnection *connection, NMSettingsConnection *self)
-{
- set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
- _emit_updated (self, FALSE);
-}
+ /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
-static gboolean
-_delete (NMSettingsConnection *self, GError **error)
-{
- NMSettingsConnectionClass *klass;
- GError *local = NULL;
- const char *filename;
+ connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
- nm_assert (NM_IS_SETTINGS_CONNECTION (self));
+ nm_connection_clear_secrets (connection_cloned);
- klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
- if (!klass->delete) {
- g_set_error (&local,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "delete not supported");
- goto fail;
- }
- if (!klass->delete (self,
- &local))
- goto fail;
-
- filename = nm_settings_connection_get_filename (self);
- if (filename) {
- _LOGD ("delete: success deleting connection (\"%s\")", filename);
- nm_settings_connection_set_filename (self, NULL);
- } else
- _LOGT ("delete: success deleting connection (no-file)");
- return TRUE;
-fail:
- _LOGD ("delete: failure deleting connection: %s", local->message);
- g_propagate_error (error, local);
- return FALSE;
+ if (!nm_settings_connection_update (self,
+ connection_cloned,
+ persist
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+ | (clear_cached_system_secrets ? NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
+ "clear-secrets",
+ NULL))
+ nm_assert_not_reached ();
}
static gboolean
-_update_prepare (NMSettingsConnection *self,
- NMConnection *new_connection,
+_secrets_update (NMConnection *connection,
+ const char *setting_name,
+ GVariant *secrets,
+ NMConnection **out_new_connection,
GError **error)
{
- g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
- g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
+ gs_unref_variant GVariant *secrets_setting = NULL;
- if (!nm_connection_normalize (new_connection, NULL, NULL, error))
- return FALSE;
+ nm_assert (NM_IS_CONNECTION (connection));
- if ( nm_dbus_object_get_path (NM_DBUS_OBJECT (self))
- && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) {
- /* Updating the UUID is not allowed once the path is exported. */
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "connection %s cannot change the UUID from %s to %s", nm_settings_connection_get_id (self),
- nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection));
+ if ( setting_name
+ && !nm_connection_get_setting_by_name (connection, setting_name)) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+ setting_name);
return FALSE;
}
- return TRUE;
-}
-
-gboolean
-nm_settings_connection_update (NMSettingsConnection *self,
- NMConnection *new_connection,
- NMSettingsConnectionPersistMode persist_mode,
- NMSettingsConnectionCommitReason commit_reason,
- const char *log_diff_name,
- GError **error)
-{
- NMSettingsConnectionPrivate *priv;
- NMSettingsConnectionClass *klass = NULL;
- gs_unref_object NMConnection *reread_connection = NULL;
- NMConnection *replace_connection;
- gboolean replaced = FALSE;
- gs_free char *logmsg_change = NULL;
- GError *local = NULL;
- gs_unref_variant GVariant *con_agent_secrets = NULL;
- gs_unref_variant GVariant *new_agent_secrets = NULL;
-
- g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-
- priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
- klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
- if (!klass->commit_changes) {
- g_set_error (&local,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "writing settings not supported");
- goto out;
- }
- }
-
- if ( new_connection
- && !_update_prepare (self,
- new_connection,
- &local))
- goto out;
-
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
- if (!klass->commit_changes (self,
- new_connection ?: nm_settings_connection_get_connection (self),
- commit_reason,
- &reread_connection,
- &logmsg_change,
- &local))
- goto out;
-
- if ( reread_connection
- && !_update_prepare (self,
- reread_connection,
- &local))
- goto out;
- }
-
- replace_connection = reread_connection ?: new_connection;
-
- /* Save agent-owned secrets from the new connection for later use */
- if (new_connection) {
- new_agent_secrets = nm_connection_to_dbus (new_connection, NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
- }
-
- /* Disconnect the changed signal to ensure we don't set Unsaved when
- * it's not required.
- */
- g_signal_handlers_block_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
- /* Do nothing if there's nothing to update */
- if ( replace_connection
- && !nm_connection_compare (nm_settings_connection_get_connection (self),
- replace_connection,
- NM_SETTING_COMPARE_FLAG_EXACT)) {
-
- if (log_diff_name) {
- nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ",
- nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
- }
-
- /* Make a copy of agent-owned secrets because they won't be present in
- * the connection returned by plugins, as plugins return only what was
- * reread from the file. */
- con_agent_secrets = nm_connection_to_dbus (nm_settings_connection_get_connection (self),
- NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
-
- nm_connection_replace_settings_from_connection (nm_settings_connection_get_connection (self), replace_connection);
-
- replaced = TRUE;
- }
+ if (!secrets)
+ return TRUE;
- nm_settings_connection_set_flags (self,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
- FALSE);
+ nm_assert ( g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING)
+ || g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION));
- if (replaced) {
- /* Cache the just-updated system secrets in case something calls
- * nm_connection_clear_secrets() and clears them.
- */
- update_system_secrets_cache (self);
+ if (g_variant_n_children (secrets) == 0)
+ return TRUE;
- /* Add agent and always-ask secrets back; they won't necessarily be
- * in the replacement connection data if it was eg reread from disk.
- */
- if (priv->agent_secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, priv->agent_secrets, NULL);
- }
- if (con_agent_secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL);
+ if ( setting_name
+ && g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION)) {
+ secrets_setting = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING);
+ if (!secrets_setting) {
+ /* The connection dictionary didn't contain any secrets for
+ * @setting_name; just return success.
+ */
+ return TRUE;
}
+ secrets = secrets_setting;
}
- /* Apply agent-owned secrets from the new connection so that
- * they can be sent to agents */
- if (new_agent_secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (self),
- NULL,
- new_agent_secrets,
- NULL);
+ /* if @out_new_connection is provided, we don't modify @connection but clone
+ * and return it. Otherwise, we update @connection inplace. */
+ if (out_new_connection) {
+ nm_assert (!*out_new_connection);
+ connection = nm_simple_connection_new_clone (connection);
+ *out_new_connection = connection;
}
- nm_settings_connection_recheck_visibility (self);
-
- if ( replaced
- && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP)
- set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
- else
- set_persist_mode (self, persist_mode);
-
- if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY))
- _delete (self, NULL);
- else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED))
- nm_settings_connection_set_filename (self, NULL);
-
- g_signal_handlers_unblock_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
- _emit_updated (self, TRUE);
-
-out:
- if (local) {
- _LOGI ("write: failure to update connection: %s", local->message);
- g_propagate_error (error, local);
+ if (!nm_connection_update_secrets (connection,
+ setting_name,
+ secrets,
+ error))
return FALSE;
- }
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
- if (reread_connection)
- _LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change);
- else if (new_connection)
- _LOGI ("write: successfully updated (%s)", logmsg_change);
- else
- _LOGI ("write: successfully committed (%s)", logmsg_change);
- }
return TRUE;
}
gboolean
-nm_settings_connection_delete (NMSettingsConnection *self,
+nm_settings_connection_update (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
GError **error)
{
- gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
- NMConnection *for_agents;
- const char *connection_uuid;
-
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
- self_keep_alive = g_object_ref (self);
-
- if (!_delete (self, error))
- return FALSE;
-
- set_visible (self, FALSE);
-
- /* Tell agents to remove secrets for this connection */
- for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
- nm_connection_clear_secrets (for_agents);
- nm_agent_manager_delete_secrets (priv->agent_mgr,
- nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
- for_agents);
- g_object_unref (for_agents);
-
- connection_uuid = nm_settings_connection_get_uuid (self);
-
- if (priv->kf_db_timestamps)
- nm_key_file_db_remove_key (priv->kf_db_timestamps, connection_uuid);
+ return nm_settings_update_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings,
+ self,
+ new_connection,
+ persist_mode,
+ sett_flags,
+ sett_mask,
+ update_reason,
+ log_context_name,
+ error);
+}
- if (priv->kf_db_seen_bssids)
- nm_key_file_db_remove_key (priv->kf_db_seen_bssids, connection_uuid);
+void
+nm_settings_connection_delete (NMSettingsConnection *self,
+ gboolean allow_add_to_no_auto_default)
+{
+ g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
- nm_settings_connection_signal_remove (self);
- return TRUE;
+ nm_settings_delete_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings,
+ self,
+ TRUE,
+ allow_add_to_no_auto_default);
}
/*****************************************************************************/
@@ -860,26 +787,37 @@ nm_settings_connection_new_secrets (NMSettingsConnection *self,
GVariant *secrets,
GError **error)
{
- if (!nm_settings_connection_has_unmodified_applied_connection (self, applied_connection,
- NM_SETTING_COMPARE_FLAG_NONE)) {
+ gs_unref_object NMConnection *new_connection = NULL;
+ NMConnection *connection;
+
+ if (!nm_settings_connection_has_unmodified_applied_connection (self,
+ applied_connection,
+ NM_SETTING_COMPARE_FLAG_NONE)) {
g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"The connection was modified since activation");
return FALSE;
}
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (!nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, secrets, error))
- return FALSE;
+ connection = nm_settings_connection_get_connection (self);
- update_system_secrets_cache (self);
- update_agent_secrets_cache (self, NULL);
+ if (!_secrets_update (connection,
+ setting_name,
+ secrets,
+ &new_connection,
+ error))
+ return FALSE;
- nm_settings_connection_update (self,
- NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "new-secrets",
- NULL);
+ if (!nm_settings_connection_update (self,
+ new_connection ?: connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+ "new-secrets",
+ NULL))
+ nm_assert_not_reached ();
return TRUE;
}
@@ -901,8 +839,10 @@ get_secrets_done_cb (NMAgentManager *manager,
NMConnection *applied_connection;
gs_free_error GError *local = NULL;
gs_unref_variant GVariant *system_secrets = NULL;
+ gs_unref_object NMConnection *new_connection = NULL;
gboolean agent_had_system = FALSE;
ForEachSecretFlags cmp_flags = { NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE };
+ gs_unref_variant GVariant *filtered_secrets = NULL;
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
@@ -965,66 +905,67 @@ get_secrets_done_cb (NMAgentManager *manager,
system_secrets = nm_g_variant_ref (priv->system_secrets);
- /* Update the connection with our existing secrets from backing storage */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
-
- if ( !system_secrets
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- || nm_connection_update_secrets (nm_settings_connection_get_connection (self),
- setting_name,
- system_secrets,
- &local)) {
- gs_unref_variant GVariant *filtered_secrets = NULL;
-
- /* Update the connection with the agent's secrets; by this point if any
- * system-owned secrets exist in 'secrets' the agent that provided them
- * will have been authenticated, so those secrets can replace the existing
- * system secrets.
- */
- filtered_secrets = validate_secret_flags (nm_settings_connection_get_connection (self), secrets, &cmp_flags);
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, filtered_secrets, &local)) {
- /* Now that all secrets are updated, copy and cache new secrets,
- * then save them to backing storage.
- */
- update_system_secrets_cache (self);
- update_agent_secrets_cache (self, NULL);
+ new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
- /* Only save secrets to backing storage if the agent returned any
- * new system secrets. If it didn't, then the secrets are agent-
- * owned and there's no point to writing out the connection when
- * nothing has changed, since agent-owned secrets don't get saved here.
- */
- if (agent_had_system) {
- _LOGD ("(%s:%p) saving new secrets to backing storage",
- setting_name,
- call_id);
-
- nm_settings_connection_update (self,
- NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "get-new-secrets",
- NULL);
- } else {
- _LOGD ("(%s:%p) new agent secrets processed",
- setting_name,
- call_id);
- }
+ nm_connection_clear_secrets (new_connection);
- } else {
- _LOGD ("(%s:%p) failed to update with agent secrets: %s",
- setting_name,
- call_id,
- local->message);
- }
- } else {
+ if (!_secrets_update (new_connection,
+ setting_name,
+ system_secrets,
+ NULL,
+ &local)) {
_LOGD ("(%s:%p) failed to update with existing secrets: %s",
setting_name,
call_id,
local->message);
}
+ /* Update the connection with the agent's secrets; by this point if any
+ * system-owned secrets exist in 'secrets' the agent that provided them
+ * will have been authenticated, so those secrets can replace the existing
+ * system secrets.
+ */
+ filtered_secrets = validate_secret_flags (new_connection, secrets, &cmp_flags);
+
+ if (!_secrets_update (new_connection,
+ setting_name,
+ filtered_secrets,
+ NULL,
+ &local)) {
+ _LOGD ("(%s:%p) failed to update with agent secrets: %s",
+ setting_name,
+ call_id,
+ local->message);
+ }
+
+ /* Only save secrets to backing storage if the agent returned any
+ * new system secrets. If it didn't, then the secrets are agent-
+ * owned and there's no point to writing out the connection when
+ * nothing has changed, since agent-owned secrets don't get saved here.
+ */
+ if (agent_had_system) {
+ _LOGD ("(%s:%p) saving new secrets to backing storage",
+ setting_name,
+ call_id);
+ } else {
+ _LOGD ("(%s:%p) new agent secrets processed",
+ setting_name,
+ call_id);
+ }
+ if (!nm_settings_connection_update (self,
+ new_connection,
+ agent_had_system
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+ "get-new-secrets",
+ NULL))
+ nm_assert_not_reached ();
+
applied_connection = call_id->applied_connection;
if (applied_connection) {
get_cmp_flags (self,
@@ -1042,10 +983,10 @@ get_secrets_done_cb (NMAgentManager *manager,
if ( !system_secrets
|| nm_connection_update_secrets (applied_connection, setting_name, system_secrets, NULL)) {
- gs_unref_variant GVariant *filtered_secrets = NULL;
+ gs_unref_variant GVariant *filtered_secrets2 = NULL;
- filtered_secrets = validate_secret_flags (applied_connection, secrets, &cmp_flags);
- nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets, NULL);
+ filtered_secrets2 = validate_secret_flags (applied_connection, secrets, &cmp_flags);
+ nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets2, NULL);
}
}
@@ -1502,10 +1443,8 @@ update_auth_cb (NMSettingsConnection *self,
{
NMSettingsConnectionPrivate *priv;
UpdateInfo *info = data;
- NMSettingsConnectionCommitReason commit_reason;
gs_free_error GError *local = NULL;
NMSettingsConnectionPersistMode persist_mode;
- const char *log_diff_name;
if (error) {
update_complete (self, info, error);
@@ -1547,34 +1486,16 @@ update_auth_cb (NMSettingsConnection *self,
}
}
- commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION;
- if ( info->new_settings
- && !nm_streq0 (nm_connection_get_id (nm_settings_connection_get_connection (self)),
- nm_connection_get_id (info->new_settings)))
- commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED;
-
if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_TO_DISK))
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
- else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY))
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
- else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)) {
- persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
- } else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
- persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED))
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+ else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
} else
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
- if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK
- || ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
- && !nm_settings_connection_get_unsaved (self)))
- log_diff_name = info->new_settings ? "update-settings" : "write-out-to-disk";
- else
- log_diff_name = info->new_settings ? "update-unsaved" : "make-unsaved";
-
if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)) {
nm_settings_connection_autoconnect_blocked_reason_set (self,
NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
@@ -1584,8 +1505,16 @@ update_auth_cb (NMSettingsConnection *self,
nm_settings_connection_update (self,
info->new_settings,
persist_mode,
- commit_reason,
- log_diff_name,
+ ( NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
+ ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+ "update-from-dbus",
&local);
if (!local) {
@@ -1604,6 +1533,9 @@ update_auth_cb (NMSettingsConnection *self,
info->subject);
}
+ /* Reset auto retries back to default since connection was updated */
+ nm_settings_connection_autoconnect_retries_reset (self);
+
update_complete (self, info, local);
}
@@ -1839,7 +1771,6 @@ delete_auth_cb (NMSettingsConnection *self,
gpointer data)
{
gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
- gs_free_error GError *local = NULL;
self_keep_alive = g_object_ref (self);
@@ -1850,15 +1781,11 @@ delete_auth_cb (NMSettingsConnection *self,
return;
}
- nm_settings_connection_delete (self, &local);
+ nm_settings_connection_delete (self, TRUE);
nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self,
- !local, NULL, subject, local ? local->message : NULL);
-
- if (local)
- g_dbus_method_invocation_return_gerror (context, local);
- else
- g_dbus_method_invocation_return_value (context, NULL);
+ TRUE, NULL, subject, NULL);
+ g_dbus_method_invocation_return_value (context, NULL);
}
static const char *
@@ -2006,26 +1933,13 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
return;
}
- /* Clear secrets in connection and caches */
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
-
- nm_clear_pointer (&priv->system_secrets, g_variant_unref);
- nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
+ nm_settings_connection_clear_secrets (self, TRUE, TRUE);
/* Tell agents to remove secrets for this connection */
nm_agent_manager_delete_secrets (priv->agent_mgr,
nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
nm_settings_connection_get_connection (self));
- nm_settings_connection_update (self,
- NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "clear-secrets",
- &local);
-
nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
!local, NULL, subject, local ? local->message : NULL);
@@ -2066,40 +1980,28 @@ impl_settings_connection_clear_secrets (NMDBusObject *obj,
/*****************************************************************************/
void
-nm_settings_connection_added (NMSettingsConnection *self)
+_nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self)
{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- /* FIXME: we should always dispose connections that are removed
- * and not reuse them, but currently plugins keep alive unmanaged
- * (e.g. NM_CONTROLLED=no) connections. */
- priv->removed = FALSE;
+ nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+ &interface_info_settings_connection,
+ &signal_info_updated,
+ "()");
}
void
-nm_settings_connection_signal_remove (NMSettingsConnection *self)
+_nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self)
{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
- AuthData *auth_data;
-
- if (priv->removed)
- return;
- priv->removed = TRUE;
-
- while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
- nm_auth_manager_check_authorization_cancel (auth_data->call_id);
-
nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
&interface_info_settings_connection,
&signal_info_removed,
"()");
- g_signal_emit (self, signals[REMOVED], 0);
}
-gboolean
-nm_settings_connection_get_unsaved (NMSettingsConnection *self)
+void
+_nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self,
+ NMSettingsConnectionUpdateReason update_reason)
{
- return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+ g_signal_emit (self, signals[UPDATED_INTERNAL], 0, (guint) update_reason);
}
/*****************************************************************************/
@@ -2121,14 +2023,6 @@ nm_settings_connection_get_flags (NMSettingsConnection *self)
}
NMSettingsConnectionIntFlags
-nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set)
-{
- return nm_settings_connection_set_flags_full (self,
- flags,
- set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
-}
-
-NMSettingsConnectionIntFlags
nm_settings_connection_set_flags_full (NMSettingsConnection *self,
NMSettingsConnectionIntFlags mask,
NMSettingsConnectionIntFlags value)
@@ -2137,7 +2031,8 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self,
NMSettingsConnectionIntFlags old_flags;
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
- nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
+
+ nm_assert (!NM_FLAGS_ANY (mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
nm_assert (!NM_FLAGS_ANY (value, ~mask));
priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
@@ -2298,17 +2193,10 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *self,
}
}
-/**
- * nm_settings_connection_read_and_fill_timestamp:
- * @self: the #NMSettingsConnection
- *
- * Retrieves timestamp of the connection's last usage from database file and
- * stores it into the connection private data.
- **/
void
-nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
- NMKeyFileDB *kf_db_timestamps,
- NMKeyFileDB *kf_db_seen_bssids)
+_nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
+ NMKeyFileDB *kf_db_timestamps,
+ NMKeyFileDB *kf_db_seen_bssids)
{
NMSettingsConnectionPrivate *priv;
const char *connection_uuid;
@@ -2625,46 +2513,6 @@ nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self)
/*****************************************************************************/
-gboolean
-nm_settings_connection_get_ready (NMSettingsConnection *self)
-{
- return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready;
-}
-
-void
-nm_settings_connection_set_ready (NMSettingsConnection *self,
- gboolean ready)
-{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- ready = !!ready;
- if (priv->ready != ready) {
- priv->ready = ready;
- _notify (self, PROP_READY);
- }
-}
-
-/**
- * nm_settings_connection_set_filename:
- * @self: an #NMSettingsConnection
- * @filename: @self's filename
- *
- * Called by a backend to sets the filename that @self is read
- * from/written to.
- */
-void
-nm_settings_connection_set_filename (NMSettingsConnection *self,
- const char *filename)
-{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- if (g_strcmp0 (filename, priv->filename) != 0) {
- g_free (priv->filename);
- priv->filename = g_strdup (filename);
- _notify (self, PROP_FILENAME);
- }
-}
-
/**
* nm_settings_connection_get_filename:
* @self: an #NMSettingsConnection
@@ -2678,9 +2526,9 @@ nm_settings_connection_set_filename (NMSettingsConnection *self,
const char *
nm_settings_connection_get_filename (NMSettingsConnection *self)
{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
- return priv->filename;
+ return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->filename;
}
const char *
@@ -2692,7 +2540,18 @@ nm_settings_connection_get_id (NMSettingsConnection *self)
const char *
nm_settings_connection_get_uuid (NMSettingsConnection *self)
{
- return nm_connection_get_uuid (nm_settings_connection_get_connection (self));
+ NMSettingsConnectionPrivate *priv;
+ const char *uuid;
+
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+ priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+ uuid = nm_settings_storage_get_uuid (priv->storage);
+
+ nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (nm_settings_connection_get_connection (self))));
+
+ return uuid;
}
const char *
@@ -2703,6 +2562,43 @@ nm_settings_connection_get_connection_type (NMSettingsConnection *self)
/*****************************************************************************/
+void
+_nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ AuthData *auth_data;
+
+ while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
+ nm_auth_manager_check_authorization_cancel (auth_data->call_id);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+
+ switch (prop_id) {
+ case PROP_UNSAVED:
+ g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint (value,
+ nm_settings_connection_get_flags (self) & _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (value, nm_settings_connection_get_filename (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
static void
nm_settings_connection_init (NMSettingsConnection *self)
{
@@ -2713,33 +2609,19 @@ nm_settings_connection_init (NMSettingsConnection *self)
c_list_init (&self->_connections_lst);
- priv->ready = TRUE;
c_list_init (&priv->call_ids_lst_head);
c_list_init (&priv->auth_lst_head);
- priv->session_monitor = g_object_ref (nm_session_monitor_get ());
- priv->session_changed_id = g_signal_connect (priv->session_monitor,
- NM_SESSION_MONITOR_CHANGED,
- G_CALLBACK (session_changed_cb), self);
-
priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+ priv->settings = g_object_ref (nm_settings_get ());
priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET;
-
- priv->connection = nm_simple_connection_new ();
-
- g_signal_connect (priv->connection, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), self);
- g_signal_connect (priv->connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), self);
}
-static void
-constructed (GObject *object)
+NMSettingsConnection *
+nm_settings_connection_new (void)
{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
-
- _LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self));
-
- G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object);
+ return g_object_new (NM_TYPE_SETTINGS_CONNECTION, NULL);
}
static void
@@ -2751,6 +2633,8 @@ dispose (GObject *object)
_LOGD ("disposing");
+ nm_assert (!priv->default_wired_device);
+
nm_assert (c_list_is_empty (&self->_connections_lst));
nm_assert (c_list_is_empty (&priv->auth_lst_head));
@@ -2760,83 +2644,29 @@ dispose (GObject *object)
_get_secrets_cancel (self, call_id, TRUE);
}
- set_visible (self, FALSE);
-
- if (priv->connection) {
- /* Disconnect handlers.
- * connection_changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
- * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
- */
- g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (secrets_cleared_cb), self);
- g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (priv->connection);
- }
-
nm_clear_pointer (&priv->system_secrets, g_variant_unref);
nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy);
- nm_clear_g_signal_handler (priv->session_monitor, &priv->session_changed_id);
- g_clear_object (&priv->session_monitor);
-
g_clear_object (&priv->agent_mgr);
g_clear_object (&priv->connection);
- g_clear_pointer (&priv->filename, g_free);
-
g_clear_pointer (&priv->kf_db_timestamps, nm_key_file_db_unref);
g_clear_pointer (&priv->kf_db_seen_bssids, nm_key_file_db_unref);
G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
-}
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
- switch (prop_id) {
- case PROP_UNSAVED:
- g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
- break;
- case PROP_READY:
- g_value_set_boolean (value, nm_settings_connection_get_ready (self));
- break;
- case PROP_FLAGS:
- g_value_set_uint (value,
- nm_settings_connection_get_flags (self) & NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
- break;
- case PROP_FILENAME:
- g_value_set_string (value, nm_settings_connection_get_filename (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
+ g_clear_object (&priv->storage);
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+ nm_clear_g_free (&priv->filename);
- switch (prop_id) {
- case PROP_FILENAME:
- /* construct-only */
- nm_settings_connection_set_filename (self, g_value_get_string (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ g_clear_object (&priv->settings);
}
+/*****************************************************************************/
+
static const GDBusSignalInfo signal_info_updated = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
"Updated",
);
@@ -2946,10 +2776,8 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS);
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings_connection);
- object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->get_property = get_property;
- object_class->set_property = set_property;
obj_properties[PROP_UNSAVED] =
g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
@@ -2957,12 +2785,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
- obj_properties[PROP_READY] =
- g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
- TRUE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
obj_properties[PROP_FLAGS] =
g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
0, G_MAXUINT32, 0,
@@ -2972,29 +2794,20 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
obj_properties[PROP_FILENAME] =
g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
- /* internal signal, with an argument (gboolean by_user). */
+ /* internal signal, with an argument (NMSettingsConnectionUpdateReason update_reason) as
+ * guint. */
signals[UPDATED_INTERNAL] =
g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- signals[REMOVED] =
- g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
signals[FLAGS_CHANGED] =
g_signal_new (NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 38e34d8fa5..80950513f8 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -21,11 +21,78 @@
#ifndef __NETWORKMANAGER_SETTINGS_CONNECTION_H__
#define __NETWORKMANAGER_SETTINGS_CONNECTION_H__
-#include <net/ethernet.h>
-
#include "nm-dbus-object.h"
#include "nm-connection.h"
+#include "nm-settings-storage.h"
+
+/*****************************************************************************/
+
+typedef enum {
+
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE = 0,
+
+ /* with persist-mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, and
+ * update tries to update the profile on disk (which can always fail).
+ * In some cases we want to ignore such failure and proceed. For example,
+ * when we receive secrets from a secret-agent, we want to update the connection
+ * at all cost and ignore failures to write them to disk. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE = (1u << 0),
+
+ /* When updating the profile, force renaming the file on disk. That matters
+ * only for keyfile plugin. Keyfile prefers a filename based on connection.id.
+ * When the connection.id changes we might want to rename the file on disk
+ * (that is, don't overwrite the existing file, but delete it and write it
+ * with the new name).
+ * This flag forces such rename. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME = (1u << 1),
+
+ /* Usually, changing a profile that is currently active does not immediately
+ * reapply the changes. The exception are connection.zone and connection.metered
+ * properties. When this flag is set, then these two properties are reapplied
+ * right away. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL = (1u << 2),
+
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS = (1u << 3),
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS = (1u << 4),
+
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS = (1u << 5),
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS = (1u << 6),
+
+ /* if a profile was greated as default-wired connection for a device, then
+ * when the user modifies it via D-Bus, the profile should become persisted
+ * to disk and it the purpose why the profile was created should be forgotten. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_DEFAULT_WIRED = (1u << 7),
+
+} NMSettingsConnectionUpdateReason;
+
+typedef enum {
+
+ /* if the profile is in-memory, update it in-memory and keep it.
+ * if the profile is on-disk, update it on-disk, and keep it. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+
+ /* persist to disk. If the profile is currenly in-memory, remove
+ * it from /run. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+
+ /* persist to /run (in-memory). If the profile is currently on disk,
+ * delete it from disk. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+
+ /* persist to /run (in-memory). If the profile is currently on disk,
+ * forget about it, but don't delete it from disk. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+
+ /* this only updates the connection in-memory. Note that "in-memory" above
+ * means to write to keyfile in /run. This really means to not notify the
+ * settings plugin about the change. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+
+} NMSettingsConnectionPersistMode;
+
+/*****************************************************************************/
+
#define NM_TYPE_SETTINGS_CONNECTION (nm_settings_connection_get_type ())
#define NM_SETTINGS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection))
#define NM_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass))
@@ -33,7 +100,6 @@
#define NM_IS_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_CONNECTION))
#define NM_SETTINGS_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass))
-#define NM_SETTINGS_CONNECTION_REMOVED "removed"
#define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets"
#define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets"
#define NM_SETTINGS_CONNECTION_UPDATED_INTERNAL "updated-internal"
@@ -44,9 +110,6 @@
#define NM_SETTINGS_CONNECTION_FLAGS "flags"
#define NM_SETTINGS_CONNECTION_FILENAME "filename"
-/* Internal properties */
-#define NM_SETTINGS_CONNECTION_READY "ready"
-
/**
* NMSettingsConnectionIntFlags:
* @NM_SETTINGS_CONNECTION_INT_FLAGS_NONE: no flag set
@@ -61,38 +124,37 @@
* currently active but cleanup on disconnect.
* See also #NM_SETTINGS_CONNECTION_FLAG_VOLATILE.
* @NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE: The connection is visible
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is
+ * @_NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is
* internal, however, parts of it is public API as #NMSettingsConnectionFlags.
* This mask, are the public flags.
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags
+ * @_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags
*
* #NMSettingsConnection flags.
**/
-typedef enum {
+typedef enum _NMSettingsConnectionIntFlags {
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE = 0,
NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED = NM_SETTINGS_CONNECTION_FLAG_UNSAVED,
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED = NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED,
NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE = NM_SETTINGS_CONNECTION_FLAG_VOLATILE,
- NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = (1LL << 3),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = 0x08,
- __NM_SETTINGS_CONNECTION_INT_FLAGS_LAST,
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_LAST,
- NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0
| NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
| NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
| NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
| 0,
- NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1,
-} NMSettingsConnectionIntFlags;
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK = 0
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | 0,
-typedef enum {
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE = 0,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION = (1LL << 0),
- NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED = (1LL << 1),
-} NMSettingsConnectionCommitReason;
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((_NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1,
+} NMSettingsConnectionIntFlags;
typedef enum {
NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE = 0,
@@ -106,7 +168,6 @@ typedef enum {
| NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS),
} NMSettingsAutoconnectBlockedReason;
-struct _NMSettingsConnectionCallId;
typedef struct _NMSettingsConnectionCallId NMSettingsConnectionCallId;
typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass;
@@ -115,27 +176,27 @@ struct _NMSettingsConnectionPrivate;
struct _NMSettingsConnection {
NMDBusObject parent;
- struct _NMSettingsConnectionPrivate *_priv;
CList _connections_lst;
+ struct _NMSettingsConnectionPrivate *_priv;
};
-struct _NMSettingsConnectionClass {
- NMDBusObjectClass parent;
+GType nm_settings_connection_get_type (void);
+
+NMSettingsConnection *nm_settings_connection_new (void);
- gboolean (*commit_changes) (NMSettingsConnection *self,
- NMConnection *new_connection,
- NMSettingsConnectionCommitReason commit_reason,
- NMConnection **out_reread_connection,
- char **out_logmsg_change,
- GError **error);
+NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self);
- gboolean (*delete) (NMSettingsConnection *self,
- GError **error);
-};
+void _nm_settings_connection_set_connection (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMConnection **out_old_connection,
+ NMSettingsConnectionUpdateReason update_reason);
-GType nm_settings_connection_get_type (void);
+NMSettingsStorage *nm_settings_connection_get_storage (NMSettingsConnection *self);
-NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self);
+void _nm_settings_connection_set_storage (NMSettingsConnection *self,
+ NMSettingsStorage *storage);
+
+const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
guint64 nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self);
@@ -143,34 +204,18 @@ gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsCon
NMConnection *applied_connection,
NMSettingCompareFlags compare_flage);
-typedef enum {
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
-
- /* like KEEP, but always clears the UNSAVED flag */
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
-
- /* unsaved, only sets the unsaved flag, but it doesn't touch
- * the NM_GENERATED nor VOLATILE flag. */
- NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED,
-
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY,
-} NMSettingsConnectionPersistMode;
-
-gboolean nm_settings_connection_update (NMSettingsConnection *self,
- NMConnection *new_connection,
- NMSettingsConnectionPersistMode persist_mode,
- NMSettingsConnectionCommitReason commit_reason,
- const char *log_diff_name,
- GError **error);
-
-gboolean nm_settings_connection_delete (NMSettingsConnection *self,
+gboolean nm_settings_connection_update (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
GError **error);
+void nm_settings_connection_delete (NMSettingsConnection *self,
+ gboolean allow_add_to_no_auto_default);
+
typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self,
NMSettingsConnectionCallId *call_id,
const char *agent_username,
@@ -196,21 +241,44 @@ NMSettingsConnectionCallId *nm_settings_connection_get_secrets (NMSettingsConnec
void nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
NMSettingsConnectionCallId *call_id);
-void nm_settings_connection_recheck_visibility (NMSettingsConnection *self);
+void nm_settings_connection_clear_secrets (NMSettingsConnection *self,
+ gboolean clear_cached_system_secrets,
+ gboolean persist);
+
+gboolean nm_settings_connection_check_visibility (NMSettingsConnection *self,
+ NMSessionMonitor *session_monitor);
gboolean nm_settings_connection_check_permission (NMSettingsConnection *self,
const char *permission);
-void nm_settings_connection_added (NMSettingsConnection *self);
+/*****************************************************************************/
-void nm_settings_connection_signal_remove (NMSettingsConnection *self);
+NMDevice *nm_settings_connection_default_wired_get_device (NMSettingsConnection *self);
+void nm_settings_connection_default_wired_set_device (NMSettingsConnection *self,
+ NMDevice *device);
-gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self);
+/*****************************************************************************/
NMSettingsConnectionIntFlags nm_settings_connection_get_flags (NMSettingsConnection *self);
-NMSettingsConnectionIntFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set);
+
+static inline gboolean
+nm_settings_connection_get_unsaved (NMSettingsConnection *self)
+{
+ return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+}
+
NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags mask, NMSettingsConnectionIntFlags value);
+static inline NMSettingsConnectionIntFlags
+nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set)
+{
+ return nm_settings_connection_set_flags_full (self,
+ flags,
+ set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
+}
+
+/*****************************************************************************/
+
int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab);
int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b);
@@ -218,9 +286,9 @@ int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer p
struct _NMKeyFileDB;
-void nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
- struct _NMKeyFileDB *kf_db_timestamps,
- struct _NMKeyFileDB *kf_db_seen_bssids);
+void _nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
+ struct _NMKeyFileDB *kf_db_timestamps,
+ struct _NMKeyFileDB *kf_db_seen_bssids);
gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self,
guint64 *out_timestamp);
@@ -259,14 +327,6 @@ nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *sel
gboolean nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self);
-gboolean nm_settings_connection_get_ready (NMSettingsConnection *self);
-void nm_settings_connection_set_ready (NMSettingsConnection *self,
- gboolean ready);
-
-void nm_settings_connection_set_filename (NMSettingsConnection *self,
- const char *filename);
-const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
-
const char *nm_settings_connection_get_id (NMSettingsConnection *connection);
const char *nm_settings_connection_get_uuid (NMSettingsConnection *connection);
const char *nm_settings_connection_get_connection_type (NMSettingsConnection *connection);
@@ -276,4 +336,14 @@ const char *nm_settings_connection_get_connection_type (NMSettingsConnection *co
NMConnection **nm_settings_connections_array_to_connections (NMSettingsConnection *const*connections,
gssize n_connections);
+/*****************************************************************************/
+
+void _nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self);
+void _nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self);
+
+void _nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self,
+ NMSettingsConnectionUpdateReason update_reason);
+
+void _nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self);
+
#endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */
diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c
index 81a168b24d..e64cc84f64 100644
--- a/src/settings/nm-settings-plugin.c
+++ b/src/settings/nm-settings-plugin.c
@@ -22,12 +22,14 @@
#include "nm-settings-plugin.h"
+#include "nm-utils.h"
+#include "nm-core-internal.h"
+
#include "nm-settings-connection.h"
/*****************************************************************************/
enum {
- CONNECTION_ADDED,
UNMANAGED_SPECS_CHANGED,
UNRECOGNIZED_SPECS_CHANGED,
@@ -41,104 +43,173 @@ G_DEFINE_TYPE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT)
/*****************************************************************************/
GSList *
-nm_settings_plugin_get_connections (NMSettingsPlugin *self)
+nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self)
{
+ NMSettingsPluginClass *klass;
+
g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections (self);
- return NULL;
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (!klass->get_unmanaged_specs)
+ return NULL;
+ return klass->get_unmanaged_specs (self);
}
-gboolean
-nm_settings_plugin_load_connection (NMSettingsPlugin *self,
- const char *filename)
+GSList *
+nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self)
{
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ NMSettingsPluginClass *klass;
+
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection (self, filename);
- return FALSE;
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (!klass->get_unrecognized_specs)
+ return NULL;
+ return klass->get_unrecognized_specs (self);
}
void
-nm_settings_plugin_reload_connections (NMSettingsPlugin *self)
+nm_settings_plugin_reload_connections (NMSettingsPlugin *self,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
{
+ NMSettingsPluginClass *klass;
+
g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));
+ g_return_if_fail (callback);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections)
- NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections (self);
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (klass->reload_connections)
+ klass->reload_connections (self, callback, user_data);
}
-GSList *
-nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self)
+gboolean
+nm_settings_plugin_load_connection (NMSettingsPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error)
{
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
-
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs (self);
- return NULL;
-}
+ NMSettingsPluginClass *klass;
-GSList *
-nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self)
-{
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
+ 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);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs (self);
- return NULL;
+ 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);
}
-/**
- * nm_settings_plugin_add_connection:
- * @self: the #NMSettingsPlugin
- * @connection: the source connection to create a plugin-specific
- * #NMSettingsConnection from
- * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
- * not save to disk
- * @error: on return, a location to store any errors that may occur
- *
- * Creates a new #NMSettingsConnection for the given source @connection. If the
- * plugin cannot handle the given connection type, it should return %NULL and
- * set @error. The plugin owns the returned object and the caller must reference
- * the object if it wishes to continue using it.
- *
- * Returns: the new #NMSettingsConnection or %NULL
- */
-NMSettingsConnection *
+gboolean
nm_settings_plugin_add_connection (NMSettingsPlugin *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
GError **error)
{
NMSettingsPluginClass *klass;
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
- g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+#if NM_MORE_ASSERTS > 5
+ nm_assert (nm_connection_verify (connection, NULL));
+#endif
+
+ NM_SET_OUT (out_storage, NULL);
+ NM_SET_OUT (out_connection, NULL);
klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
if (!klass->add_connection) {
g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
- "Plugin does not support adding connections");
- return NULL;
+ "settings plugin does not support adding connections");
+ return FALSE;
}
-
- return klass->add_connection (self, connection, save_to_disk, error);
+ return klass->add_connection (self,
+ connection,
+ out_storage,
+ out_connection,
+ error);
}
-/*****************************************************************************/
+gboolean
+nm_settings_plugin_update_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMSettingsPluginClass *klass = NULL;
-void
-_nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *self,
- NMSettingsConnection *sett_conn)
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE);
+ g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+#if NM_MORE_ASSERTS > 5
+ nm_assert (nm_connection_verify (connection, NULL));
+ nm_assert (nm_streq (nm_connection_get_uuid (connection), nm_settings_storage_get_uuid (storage)));
+#endif
+
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+
+ NM_SET_OUT (out_storage, NULL);
+ NM_SET_OUT (out_connection, NULL);
+
+ if (!klass->update_connection) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_NOT_SUPPORTED,
+ "settings plugin does not support modifying connections");
+ return FALSE;
+ }
+ return klass->update_connection (self,
+ storage,
+ connection,
+ out_storage,
+ out_connection,
+ error);
+}
+
+gboolean
+nm_settings_plugin_delete_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error)
{
- nm_assert (NM_IS_SETTINGS_PLUGIN (self));
- nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+ NMSettingsPluginClass *klass = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE);
+ g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE);
+
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+
+ if (!klass->delete_connection) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_NOT_SUPPORTED,
+ "settings plugin does not support deleting connections");
+ return FALSE;
+ }
- g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
+ return klass->delete_connection (self,
+ storage,
+ remove_from_disk,
+ error);
}
+/*****************************************************************************/
+
void
_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self)
{
@@ -167,15 +238,6 @@ nm_settings_plugin_class_init (NMSettingsPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- signals[CONNECTION_ADDED] =
- g_signal_new (NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1,
- NM_TYPE_SETTINGS_CONNECTION);
-
signals[UNMANAGED_SPECS_CHANGED] =
g_signal_new (NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index 11b859978a..a214d7c026 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -23,6 +23,15 @@
#include "nm-connection.h"
+#include "nm-settings-storage.h"
+
+typedef struct _NMSettingsPlugin NMSettingsPlugin;
+
+typedef void (*NMSettingsPluginConnectionReloadCallback) (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gpointer user_data);
+
#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))
#define NM_SETTINGS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginClass))
@@ -32,32 +41,14 @@
#define NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed"
#define NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED "unrecognized-specs-changed"
-#define NM_SETTINGS_PLUGIN_CONNECTION_ADDED "connection-added"
-typedef struct {
+struct _NMSettingsPlugin {
GObject parent;
-} NMSettingsPlugin;
+};
typedef struct {
GObjectClass parent;
- /* Returns a GSList of NMSettingsConnection objects that represent
- * connections the plugin knows about. The returned list is freed by the
- * system settings service.
- */
- GSList * (*get_connections) (NMSettingsPlugin *plugin);
-
- /* Requests that the plugin load/reload a single connection, if it
- * recognizes the filename. Returns success or failure.
- */
- gboolean (*load_connection) (NMSettingsPlugin *plugin,
- const char *filename);
-
- /* Requests that the plugin reload all connection files from disk,
- * and emit signals reflecting new, changed, and removed connections.
- */
- void (*reload_connections) (NMSettingsPlugin *plugin);
-
/*
* Return a string list of specifications of devices which NetworkManager
* should not manage. Returned list will be freed by the system settings
@@ -67,7 +58,7 @@ typedef struct {
* Each string in the list must be in one of the formats recognized by
* nm_device_spec_match_list().
*/
- GSList * (*get_unmanaged_specs) (NMSettingsPlugin *plugin);
+ GSList * (*get_unmanaged_specs) (NMSettingsPlugin *self);
/*
* Return a string list of specifications of devices for which at least
@@ -79,49 +70,129 @@ typedef struct {
* Each string in the list must be in one of the formats recognized by
* nm_device_spec_match_list().
*/
- GSList * (*get_unrecognized_specs) (NMSettingsPlugin *plugin);
+ GSList * (*get_unrecognized_specs) (NMSettingsPlugin *self);
- /*
- * Initialize the plugin-specific connection and return a new
- * NMSettingsConnection subclass that contains the same settings as the
- * original connection. The connection should only be saved to backing
- * storage if @save_to_disk is TRUE. The returned object is owned by the
- * plugin and must be referenced by the owner if necessary.
+ /* Requests that the plugin load/reload a single connection, if it
+ * recognizes the filename. Returns success or failure.
+ */
+ gboolean (*load_connection) (NMSettingsPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error);
+
+ /* Requests that the plugin reload all connection files from disk,
+ * and emit signals reflecting new, changed, and removed connections.
*/
- NMSettingsConnection * (*add_connection) (NMSettingsPlugin *plugin,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error);
+ void (*reload_connections) (NMSettingsPlugin *self,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data);
+
+ gboolean (*add_connection) (NMSettingsPlugin *self,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+ gboolean (*update_connection) (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+ gboolean (*delete_connection) (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error);
+
+ const char *plugin_name;
+
} NMSettingsPluginClass;
+/*****************************************************************************/
+
GType nm_settings_plugin_get_type (void);
-typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void);
+/*****************************************************************************/
-/* Plugin's factory function that returns a #NMSettingsPlugin */
-NMSettingsPlugin *nm_settings_plugin_factory (void);
+#define NM_SETTINGS_STORAGE_PRINT_FMT \
+ NM_HASH_OBFUSCATE_PTR_FMT"/%s"
+
+#define NM_SETTINGS_STORAGE_PRINT_ARG(storage) \
+ NM_HASH_OBFUSCATE_PTR (storage), \
+ nm_settings_plugin_get_plugin_name (nm_settings_storage_get_plugin (storage))
+
+static inline const char *
+nm_settings_plugin_get_plugin_name (NMSettingsPlugin *self)
+{
+ NMSettingsPluginClass *klass;
+
+ nm_assert (NM_SETTINGS_PLUGIN (self));
+
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+
+ nm_assert (klass && klass->plugin_name && strlen (klass->plugin_name) > 0);
+
+ return klass->plugin_name;
+}
-GSList *nm_settings_plugin_get_connections (NMSettingsPlugin *plugin);
+/*****************************************************************************/
-gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *plugin,
- const char *filename);
-void nm_settings_plugin_reload_connections (NMSettingsPlugin *plugin);
+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,
+ 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);
+
+gboolean nm_settings_plugin_add_connection (NMSettingsPlugin *self,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+gboolean nm_settings_plugin_update_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+gboolean nm_settings_plugin_delete_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error);
+
+/*****************************************************************************/
+
+typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void);
+
+NMSettingsPlugin *nm_settings_plugin_factory (void);
-GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *plugin);
-GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *plugin);
+/*****************************************************************************
+ * Internal API
+ *****************************************************************************/
-NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *plugin,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error);
+void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self);
-/* internal API */
+void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *self);
-void _nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *plugin,
- NMSettingsConnection *sett_conn);
+/*****************************************************************************/
-void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *plugin);
+/* forward declare this function from NMSettings. It's used by the ifcfg-rh plugin,
+ * but that shouldn't include all "nm-settings.h" header. */
+NMSettings *nm_settings_get (void);
-void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *plugin);
+const char *nm_settings_get_dbus_path_for_uuid (NMSettings *self,
+ const char *uuid);
#endif /* __NM_SETTINGS_PLUGIN_H__ */
diff --git a/src/settings/nm-settings-storage.c b/src/settings/nm-settings-storage.c
new file mode 100644
index 0000000000..d07da2ca39
--- /dev/null
+++ b/src/settings/nm-settings-storage.c
@@ -0,0 +1,242 @@
+/*
+ * 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 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-settings-storage.h"
+
+#include "nm-utils.h"
+#include "nm-settings-plugin.h"
+
+#include "settings/plugins/keyfile/nms-keyfile-storage.h"
+
+/*****************************************************************************/
+
+int
+nm_settings_storage_cmp (NMSettingsStorage *a,
+ NMSettingsStorage *b,
+ const GSList *plugin_list)
+{
+ NMSettingsStorageClass *klass;
+ NMSettingsPlugin *plugin_a;
+ NMSettingsPlugin *plugin_b;
+
+ /* Sort by priority.
+ *
+ * If a > b (by priority), we return a positive number (as one
+ * would expect by a cmp() function). */
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (a));
+ nm_assert (NM_IS_SETTINGS_STORAGE (b));
+ nm_assert (a != b);
+ 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));
+
+ plugin_a = nm_settings_storage_get_plugin (a);
+ plugin_b = nm_settings_storage_get_plugin (b);
+
+ if (plugin_a != plugin_b) {
+ int idx_a = g_slist_index ((GSList *) plugin_list, plugin_a);
+ int idx_b = g_slist_index ((GSList *) plugin_list, plugin_b);
+
+ nm_assert (idx_a >= 0);
+ nm_assert (idx_b >= 0);
+ nm_assert (idx_a != idx_b);
+
+ /* plugins that appear first in @plugin_list have higher priority.
+ * That means: smaller index -> higher priority. Reverse sort. */
+ NM_CMP_DIRECT (idx_b, idx_a);
+
+ /* undecided. We really don't expect unknown plugins here. */
+ 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)) {
+ nm_assert_not_reached ();
+ return 0;
+ }
+
+ if (klass->storage_cmp)
+ NM_CMP_RETURN (klass->storage_cmp (a, b));
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsStorage,
+ PROP_PLUGIN,
+ PROP_UUID,
+ PROP_FILENAME,
+);
+
+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)
+{
+ NMSettingsStorage *self = NM_SETTINGS_STORAGE (object);
+
+ switch (prop_id) {
+ case PROP_PLUGIN:
+ /* construct-only */
+ self->_plugin = g_object_ref (g_value_get_object (value));
+ nm_assert (NM_IS_SETTINGS_PLUGIN (self->_plugin));
+ break;
+ case PROP_UUID:
+ /* construct-only */
+ self->_uuid = g_value_dup_string (value);
+ nm_assert (nm_utils_is_uuid (self->_uuid));
+ break;
+ case PROP_FILENAME:
+ /* construct-only */
+ self->_filename = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_settings_storage_init (NMSettingsStorage *self)
+{
+}
+
+NMSettingsStorage *
+nm_settings_storage_new (NMSettingsPlugin *plugin,
+ const char *uuid,
+ const char *filename)
+{
+ nm_assert (NM_IS_SETTINGS_PLUGIN (plugin));
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ return g_object_new (NM_TYPE_SETTINGS_STORAGE,
+ NM_SETTINGS_STORAGE_PLUGIN, plugin,
+ NM_SETTINGS_STORAGE_UUID, uuid,
+ NM_SETTINGS_STORAGE_FILENAME, filename,
+ NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingsStorage *self = NM_SETTINGS_STORAGE (object);
+
+ g_object_unref (self->_plugin);
+ g_free (self->_uuid);
+ g_free (self->_filename);
+
+ G_OBJECT_CLASS (nm_settings_storage_parent_class)->finalize (object);
+}
+
+static void
+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;
+
+ obj_properties[PROP_PLUGIN] =
+ g_param_spec_object (NM_SETTINGS_STORAGE_PLUGIN, "", "",
+ NM_TYPE_SETTINGS_PLUGIN,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_UUID] =
+ g_param_spec_string (NM_SETTINGS_STORAGE_UUID, "", "",
+ NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_FILENAME] =
+ g_param_spec_string (NM_SETTINGS_STORAGE_FILENAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
diff --git a/src/settings/nm-settings-storage.h b/src/settings/nm-settings-storage.h
new file mode 100644
index 0000000000..7164a15753
--- /dev/null
+++ b/src/settings/nm-settings-storage.h
@@ -0,0 +1,117 @@
+/*
+ * 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 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTINGS_STORAGE_H__
+#define __NM_SETTINGS_STORAGE_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))
+#define NM_IS_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTINGS_STORAGE))
+#define NM_IS_SETTINGS_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_STORAGE))
+#define NM_SETTINGS_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass))
+
+#define NM_SETTINGS_STORAGE_PLUGIN "plugin"
+#define NM_SETTINGS_STORAGE_UUID "uuid"
+#define NM_SETTINGS_STORAGE_FILENAME "filename"
+
+struct _NMSettingsPlugin;
+
+typedef struct NMSettingsStorage {
+ GObject parent;
+ struct _NMSettingsPlugin *_plugin;
+ char *_uuid;
+ char *_filename;
+} NMSettingsStorage;
+
+typedef struct {
+ GObjectClass parent;
+
+ int (*storage_cmp) (NMSettingsStorage *a,
+ NMSettingsStorage *b);
+
+} NMSettingsStorageClass;
+
+GType nm_settings_storage_get_type (void);
+
+NMSettingsStorage *nm_settings_storage_new (struct _NMSettingsPlugin *plugin,
+ const char *uuid,
+ const char *filename);
+
+static inline struct _NMSettingsPlugin *
+nm_settings_storage_get_plugin (NMSettingsStorage *self)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL);
+
+ return self->_plugin;
+}
+
+static inline const char *
+nm_settings_storage_get_uuid (NMSettingsStorage *self)
+{
+ gboolean nm_utils_is_uuid (const char *str);
+
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL);
+
+ nm_assert (nm_utils_is_uuid (self->_uuid));
+ return self->_uuid;
+}
+
+static inline const char *
+nm_settings_storage_get_filename (NMSettingsStorage *self)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL);
+
+ return self->_filename;
+}
+
+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)
+{
+ /* 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;
+}
+
+/*****************************************************************************/
+
+#define nm_assert_valid_settings_storage(plugin, storage) \
+ G_STMT_START { \
+ NMSettingsPlugin *const _plugin = (plugin); \
+ NMSettingsStorage *const _storage = (storage); \
+ \
+ nm_assert (!_plugin || NM_IS_SETTINGS_PLUGIN (_plugin)); \
+ nm_assert (NM_IS_SETTINGS_STORAGE (_storage)); \
+ nm_assert (!_plugin || nm_settings_storage_get_plugin (_storage) == _plugin); \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+int nm_settings_storage_cmp (NMSettingsStorage *sd_a,
+ NMSettingsStorage *sd_b,
+ const GSList *plugin_list);
+
+#endif /* __NM_SETTINGS_STORAGE_H__ */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 6fbda5274b..3086601fad 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -60,6 +60,7 @@
#include "nm-utils.h"
#include "nm-core-internal.h"
+#include "nm-std-aux/c-list-util.h"
#include "nm-glib-aux/nm-c-list.h"
#include "nm-dbus-object.h"
#include "devices/nm-device-ethernet.h"
@@ -70,6 +71,7 @@
#include "nm-auth-subject.h"
#include "nm-session-monitor.h"
#include "plugins/keyfile/nms-keyfile-plugin.h"
+#include "plugins/keyfile/nms-keyfile-storage.h"
#include "nm-agent-manager.h"
#include "nm-config.h"
#include "nm-audit-manager.h"
@@ -79,15 +81,154 @@
/*****************************************************************************/
-#define EXPORT(sym) void * __export_##sym = &sym;
+static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
+
+/*****************************************************************************/
+
+typedef struct _StorageData {
+ CList sd_lst;
+ NMSettingsStorage *storage;
+ NMConnection *connection;
+ bool prioritize:1;
+} StorageData;
+
+static StorageData *
+_storage_data_new_stale (NMSettingsStorage *storage,
+ NMConnection *connection)
+{
+ StorageData *sd;
+
+ sd = g_slice_new (StorageData);
+ sd->storage = g_object_ref (storage);
+ sd->connection = nm_g_object_ref (connection);
+ return sd;
+}
+
+static void
+_storage_data_destroy (StorageData *sd)
+{
+ c_list_unlink_stale (&sd->sd_lst);
+ g_object_unref (sd->storage);
+ nm_g_object_unref (sd->connection);
+ g_slice_free (StorageData, sd);
+}
+
+static StorageData *
+_storage_data_find_in_lst (CList *head,
+ NMSettingsStorage *storage)
+{
+ StorageData *sd;
+
+ nm_assert (head);
+ nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+
+ c_list_for_each_entry (sd, head, sd_lst) {
+ if (sd->storage == storage)
+ return sd;
+ }
+ return NULL;
+}
+
+static void
+nm_assert_storage_data_lst (CList *head)
+{
+#if NM_MORE_ASSERTS > 5
+ const char *uuid = NULL;
+ StorageData *sd;
+ CList *iter;
+
+ nm_assert (head);
+
+ if (c_list_is_empty (head))
+ return;
+
+ c_list_for_each_entry (sd, head, sd_lst) {
+ const char *u;
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage));
+ nm_assert (!sd->connection || NM_IS_CONNECTION (sd->connection));
+ u = nm_settings_storage_get_uuid (sd->storage);
+ if (!uuid) {
+ uuid = u;
+ nm_assert (nm_utils_is_uuid (uuid));
+ } else
+ nm_assert (nm_streq0 (uuid, u));
+ }
+
+ /* assert that all storages are unique. */
+ c_list_for_each_entry (sd, head, sd_lst) {
+ for (iter = sd->sd_lst.next; iter != head; iter = iter->next)
+ nm_assert (c_list_entry (iter, StorageData, sd_lst)->storage != sd->storage);
+ }
+#endif
+}
-EXPORT(nm_settings_connection_get_type)
-EXPORT(nm_settings_connection_update)
+static gboolean
+_storage_data_is_alive (StorageData *sd)
+{
+ if (sd->connection)
+ return TRUE;
+
+ if (nm_settings_storage_is_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;
+ }
+
+ return FALSE;
+}
/*****************************************************************************/
-static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
-static NM_CACHED_QUARK_FCN ("default-wired-device", _default_wired_device_quark)
+typedef struct {
+ const char *uuid;
+ NMSettingsConnection *sett_conn;
+ NMSettingsStorage *storage;
+ CList sd_lst_head;
+ CList dirty_sd_lst_head;
+
+ CList sce_dirty_lst;
+
+ char _uuid_data[];
+} SettConnEntry;
+
+static SettConnEntry *
+_sett_conn_entry_new (const char *uuid)
+{
+ SettConnEntry *sett_conn_entry;
+ gsize l_p_1;
+
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ l_p_1 = strlen (uuid) + 1;
+
+ sett_conn_entry = g_malloc (sizeof (SettConnEntry) + l_p_1);
+ sett_conn_entry->uuid = sett_conn_entry->_uuid_data;
+ sett_conn_entry->sett_conn = NULL;
+ sett_conn_entry->storage = NULL;
+ c_list_init (&sett_conn_entry->sd_lst_head);
+ c_list_init (&sett_conn_entry->dirty_sd_lst_head);
+ c_list_init (&sett_conn_entry->sce_dirty_lst);
+ memcpy (sett_conn_entry->_uuid_data, uuid, l_p_1);
+ return sett_conn_entry;
+}
+
+static void
+_sett_conn_entry_free (SettConnEntry *sett_conn_entry)
+{
+ c_list_unlink_stale (&sett_conn_entry->sce_dirty_lst);
+ nm_c_list_free_all (&sett_conn_entry->sd_lst_head, StorageData, sd_lst, _storage_data_destroy);
+ nm_c_list_free_all (&sett_conn_entry->dirty_sd_lst_head, StorageData, sd_lst, _storage_data_destroy);
+ nm_g_object_unref (sett_conn_entry->sett_conn);
+ nm_g_object_unref (sett_conn_entry->storage);
+ g_free (sett_conn_entry);
+}
+
+static NMSettingsConnection *
+_sett_conn_entry_get_conn (SettConnEntry *sett_conn_entry)
+{
+ return sett_conn_entry ? sett_conn_entry->sett_conn : NULL;
+}
/*****************************************************************************/
@@ -114,8 +255,12 @@ typedef struct {
NMConfig *config;
+ NMPlatform *platform;
+
NMHostnameManager *hostname_manager;
+ NMSessionMonitor *session_monitor;
+
CList auth_lst_head;
NMSKeyfilePlugin *keyfile_plugin;
@@ -125,6 +270,10 @@ typedef struct {
NMKeyFileDB *kf_db_timestamps;
NMKeyFileDB *kf_db_seen_bssids;
+ GHashTable *sce_idx;
+
+ CList sce_dirty_lst_head;
+
CList connections_lst_head;
NMSettingsConnection **connections_cached_list;
@@ -132,16 +281,19 @@ typedef struct {
GSList *unmanaged_specs;
GSList *unrecognized_specs;
+ GHashTable *startup_complete_idx;
NMSettingsConnection *startup_complete_blocked_by;
+ gulong startup_complete_platform_change_id;
+ guint startup_complete_timeout_id;
guint connections_len;
+ guint connections_generation;
+
guint kf_db_flush_idle_id_timestamps;
guint kf_db_flush_idle_id_seen_bssids;
bool started:1;
- bool startup_complete:1;
- bool connections_loaded:1;
} NMSettingsPrivate;
@@ -160,6 +312,9 @@ G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT);
/*****************************************************************************/
+/* FIXME: a lot of logging lines are directly connected to a profile. Set the @con_uuid
+ * argument for structured logging. */
+
#define _NMLOG_DOMAIN LOGD_SETTINGS
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "settings", __VA_ARGS__)
@@ -169,57 +324,267 @@ static const NMDBusInterfaceInfoExtended interface_info_settings;
static const GDBusSignalInfo signal_info_new_connection;
static const GDBusSignalInfo signal_info_connection_removed;
-static void claim_connection (NMSettings *self,
- NMSettingsConnection *connection);
-
-static void connection_ready_changed (NMSettingsConnection *conn,
- GParamSpec *pspec,
- gpointer user_data);
-
static void default_wired_clear_tag (NMSettings *self,
NMDevice *device,
- NMSettingsConnection *connection,
+ NMSettingsConnection *sett_conn,
gboolean add_to_no_auto_default);
static void _clear_connections_cached_list (NMSettingsPrivate *priv);
+static void _startup_complete_check (NMSettings *self,
+ gint64 now_us);
+
/*****************************************************************************/
static void
-check_startup_complete (NMSettings *self)
+_emit_connection_added (NMSettings *self,
+ NMSettingsConnection *sett_conn)
{
- NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
+}
+
+static void
+_emit_connection_updated (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ NMSettingsConnectionUpdateReason update_reason)
+{
+ _nm_settings_connection_emit_signal_updated_internal (sett_conn, update_reason);
+ g_signal_emit (self, signals[CONNECTION_UPDATED], 0, sett_conn, (guint) update_reason);
+}
+
+static void
+_emit_connection_removed (NMSettings *self,
+ NMSettingsConnection *sett_conn)
+{
+ g_signal_emit (self, signals[CONNECTION_REMOVED], 0, sett_conn);
+}
+
+static void
+_emit_connection_flags_changed (NMSettings *self,
+ NMSettingsConnection *sett_conn)
+{
+ g_signal_emit (self, signals[CONNECTION_FLAGS_CHANGED], 0, sett_conn);
+}
+
+/*****************************************************************************/
+
+typedef struct {
NMSettingsConnection *sett_conn;
+ gint64 start_at;
+ gint64 timeout;
+} StartupCompleteData;
+
+static void
+_startup_complete_data_destroy (StartupCompleteData *scd)
+{
+ g_object_unref (scd->sett_conn);
+ g_slice_free (StartupCompleteData, scd);
+}
+
+static gboolean
+_startup_complete_check_is_ready (NMPlatform *platform,
+ NMSettingsConnection *sett_conn)
+{
+ const NMPlatformLink *plink;
+ const char *ifname;
+
+ /* FIXME: instead of just looking for the interface name, it would be better
+ * to wait for a device that is compatible with the profile. */
+
+ ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn));
+
+ if (!ifname)
+ return TRUE;
+
+ plink = nm_platform_link_get_by_ifname (platform, ifname);
+ return plink && plink->initialized;
+}
- if (priv->startup_complete)
+static gboolean
+_startup_complete_timeout_cb (gpointer user_data)
+{
+ NMSettings *self = user_data;
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+
+ priv->startup_complete_timeout_id = 0;
+ _startup_complete_check (self, 0);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+_startup_complete_platform_change_cb (NMPlatform *platform,
+ int obj_type_i,
+ int ifindex,
+ const NMPlatformLink *link,
+ int change_type_i,
+ NMSettings *self)
+{
+ const NMPlatformSignalChangeType change_type = change_type_i;
+ NMSettingsPrivate *priv;
+ const char *ifname;
+
+ if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
return;
- c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) {
- if (!nm_settings_connection_get_ready (sett_conn)) {
- nm_g_object_ref_set (&priv->startup_complete_blocked_by, sett_conn);
- return;
+ if (!link->initialized)
+ return;
+
+ priv = NM_SETTINGS_GET_PRIVATE (self);
+
+ ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by));
+ if ( ifname
+ && !nm_streq (ifname, link->name))
+ return;
+
+ nm_assert (priv->startup_complete_timeout_id > 0);
+
+ nm_clear_g_source (&priv->startup_complete_timeout_id);
+ priv->startup_complete_timeout_id = g_idle_add (_startup_complete_timeout_cb, self);
+}
+
+static void
+_startup_complete_check (NMSettings *self,
+ gint64 now_us)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gint64 next_expiry;
+ StartupCompleteData *scd;
+ NMSettingsConnection *next_sett_conn = NULL;
+ GHashTableIter iter;
+
+ if (!priv->started) {
+ /* before we are started, we don't setup the timers... */
+ return;
+ }
+
+ if (!priv->startup_complete_idx)
+ goto ready;
+
+ if (!now_us)
+ now_us = nm_utils_get_monotonic_timestamp_us ();
+
+ next_expiry = 0;
+
+ g_hash_table_iter_init (&iter, priv->startup_complete_idx);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &scd, NULL)) {
+ gint64 expiry;
+
+ if (scd->start_at == 0) {
+ /* once ready, the decision is remembered and there is nothing
+ * left to check. */
+ continue;
+ }
+
+ expiry = scd->start_at + scd->timeout;
+ if (expiry <= now_us) {
+ scd->start_at = 0;
+ continue;
+ }
+
+ if (_startup_complete_check_is_ready (priv->platform, scd->sett_conn)) {
+ scd->start_at = 0;
+ continue;
}
+
+ next_expiry = expiry;
+ next_sett_conn = scd->sett_conn;
+ /* we found one timeout for which to wait. that's good enough. */
+ break;
}
- g_clear_object (&priv->startup_complete_blocked_by);
+ nm_clear_g_source (&priv->startup_complete_timeout_id);
+ nm_g_object_ref_set (&priv->startup_complete_blocked_by, next_sett_conn);
+ if (next_expiry > 0) {
+ nm_assert (priv->startup_complete_blocked_by);
+ if (priv->startup_complete_platform_change_id == 0) {
+ priv->startup_complete_platform_change_id = g_signal_connect (priv->platform,
+ NM_PLATFORM_SIGNAL_LINK_CHANGED,
+ G_CALLBACK (_startup_complete_platform_change_cb),
+ self);
+ }
+ priv->startup_complete_timeout_id = g_timeout_add (NM_MIN (3600u*1000u, (next_expiry - now_us) / 1000u),
+ _startup_complete_timeout_cb,
+ self);
+ _LOGT ("startup-complete: wait for device \"%s\" due to connection %s (%s)",
+ nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)),
+ nm_settings_connection_get_uuid (priv->startup_complete_blocked_by),
+ nm_settings_connection_get_id (priv->startup_complete_blocked_by));
+ return;
+ }
- /* the connection_ready_changed signal handler is no longer needed. */
- c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst)
- g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_ready_changed), self);
+ nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
+ nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id);
- priv->startup_complete = TRUE;
+ready:
+ _LOGT ("startup-complete: ready, no profiles to wait for");
+ nm_assert (priv->started);
+ nm_assert (!priv->startup_complete_blocked_by);
+ nm_assert (!priv->startup_complete_idx);
+ nm_assert (priv->startup_complete_timeout_id == 0);
+ nm_assert (priv->startup_complete_platform_change_id == 0);
_notify (self, PROP_STARTUP_COMPLETE);
}
static void
-connection_ready_changed (NMSettingsConnection *conn,
- GParamSpec *pspec,
- gpointer user_data)
+_startup_complete_notify_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ gboolean forget)
{
- NMSettings *self = NM_SETTINGS (user_data);
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gint64 timeout;
+ gint64 now_us = 0;
+
+ nm_assert ( !priv->started
+ || priv->startup_complete_idx);
+
+ timeout = 0;
+ if (!forget) {
+ NMSettingConnection *s_con;
+ gint32 v;
+
+ s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn));
+ v = nm_setting_connection_get_wait_device_timeout (s_con);
+ if (v > 0) {
+ nm_assert (nm_setting_connection_get_interface_name (s_con));
+ timeout = ((gint64) v) * 1000;
+ }
+ }
+
+ if (timeout == 0) {
+ if ( !priv->startup_complete_idx
+ || !g_hash_table_remove (priv->startup_complete_idx, &sett_conn))
+ return;
+ } else {
+ StartupCompleteData *scd;
+
+ if (!priv->startup_complete_idx) {
+ nm_assert (!priv->started);
+ priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash,
+ nm_pdirect_equal,
+ NULL,
+ (GDestroyNotify) _startup_complete_data_destroy);
+ scd = NULL;
+ } else
+ scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn);
+ if (!scd) {
+ now_us = nm_utils_get_monotonic_timestamp_us ();
+ scd = g_slice_new (StartupCompleteData);
+ *scd = (StartupCompleteData) {
+ .sett_conn = g_object_ref (sett_conn),
+ .start_at = now_us,
+ .timeout = timeout,
+ };
+ g_hash_table_add (priv->startup_complete_idx, scd);
+ } else {
+ if (scd->start_at == 0) {
+ /* the entry already is ready and no longer relevant. Ignore it. */
+ return;
+ }
+ scd->timeout = timeout;
+ }
+ }
- if (nm_settings_connection_get_ready (conn))
- check_startup_complete (self);
+ _startup_complete_check (self, now_us);
}
const char *
@@ -228,10 +593,12 @@ nm_settings_get_startup_complete_blocked_reason (NMSettings *self)
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
const char *uuid = NULL;
- if (priv->startup_complete)
- return NULL;
- if (priv->startup_complete_blocked_by)
- uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by);
+ if (priv->started) {
+ if (!priv->startup_complete_idx)
+ return NULL;
+ if (priv->startup_complete_blocked_by)
+ uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by);
+ }
return uuid ?: "unknown";
}
@@ -283,8 +650,8 @@ update_specs (NMSettings *self, GSList **specs_ptr,
}
static void
-unmanaged_specs_changed (NMSettingsPlugin *config,
- gpointer user_data)
+_plugin_unmanaged_specs_changed (NMSettingsPlugin *config,
+ gpointer user_data)
{
NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -295,8 +662,8 @@ unmanaged_specs_changed (NMSettingsPlugin *config,
}
static void
-unrecognized_specs_changed (NMSettingsPlugin *config,
- gpointer user_data)
+_plugin_unrecognized_specs_changed (NMSettingsPlugin *config,
+ gpointer user_data)
{
NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -308,320 +675,1103 @@ unrecognized_specs_changed (NMSettingsPlugin *config,
/*****************************************************************************/
static void
-plugin_connection_added (NMSettingsPlugin *config,
- NMSettingsConnection *connection,
- NMSettings *self)
+connection_flags_changed (NMSettingsConnection *sett_conn,
+ gpointer user_data)
+{
+ _emit_connection_flags_changed (NM_SETTINGS (user_data), sett_conn);
+}
+
+/*****************************************************************************/
+
+static SettConnEntry *
+_sett_conn_entries_get (NMSettings *self,
+ const char *uuid)
{
- claim_connection (self, connection);
+ nm_assert (nm_utils_is_uuid (uuid));
+ return g_hash_table_lookup (NM_SETTINGS_GET_PRIVATE (self)->sce_idx, &uuid);
+}
+
+static SettConnEntry *
+_sett_conn_entries_create_and_add (NMSettings *self,
+ const char *uuid)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ SettConnEntry *sett_conn_entry;
+
+ sett_conn_entry = _sett_conn_entry_new (uuid);
+
+ if (!g_hash_table_add (priv->sce_idx, sett_conn_entry))
+ nm_assert_not_reached ();
+ else if (g_hash_table_size (priv->sce_idx) == 1)
+ g_object_ref (self);
+
+ return sett_conn_entry;
}
static void
-load_connections (NMSettings *self)
+_sett_conn_entries_remove_and_destroy (NMSettings *self,
+ SettConnEntry *sett_conn_entry)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
- GSList *iter;
- for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
- GSList *plugin_connections;
- GSList *elt;
+ if (!g_hash_table_remove (priv->sce_idx, sett_conn_entry))
+ nm_assert_not_reached ();
+ else if (g_hash_table_size (priv->sce_idx) == 0)
+ g_object_unref (self);
+}
- plugin_connections = nm_settings_plugin_get_connections (plugin);
+/*****************************************************************************/
- // FIXME: ensure connections from plugins loaded with a lower priority
- // get rejected when they conflict with connections from a higher
- // priority plugin.
+static int
+_sett_conn_entry_sds_update_cmp (const CList *ls_a,
+ const CList *ls_b,
+ gconstpointer user_data)
+{
+ StorageData *sd_a = c_list_entry (ls_a, StorageData, sd_lst);
+ StorageData *sd_b = c_list_entry (ls_b, StorageData, sd_lst);
+
+ /* prioritized entries are sorted first (higher priority). */
+ NM_CMP_FIELD_UNSAFE (sd_b, sd_a, prioritize);
+
+ /* nm_settings_storage_cmp() compares in ascending order. Meaning,
+ * if the storage has higher priority, it gives a positive number (as one
+ * would expect).
+ *
+ * We want to sort the list in reverse though, with highest priority first. */
+ return nm_settings_storage_cmp (sd_b->storage, sd_a->storage, user_data);
+}
- for (elt = plugin_connections; elt; elt = g_slist_next (elt))
- claim_connection (self, elt->data);
+static void
+_sett_conn_entry_sds_update (NMSettings *self,
+ SettConnEntry *sett_conn_entry)
+{
+ StorageData *sd;
+ StorageData *sd_safe;
+ StorageData *sd_dirty;
+ gboolean reprioritize;
+
+ nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head);
+ nm_assert_storage_data_lst (&sett_conn_entry->dirty_sd_lst_head);
+
+ /* we merge the dirty list with the previous list.
+ *
+ * The idea is:
+ *
+ * - _connection_changed_track() appends events for the same UUID. Meaning:
+ * if the storage is new, it get appended (having lower priority).
+ * If it already exist and is an update for an event that we already
+ * track it, it keeps the list position in @dirty_sd_lst_head unchanged.
+ *
+ * - during merge, we want to preserve the previous order (with higher
+ * priority first in the list).
+ */
- g_slist_free (plugin_connections);
+ /* first go through all storages that we track and check whether they
+ * got an update...*/
- g_signal_connect (plugin, NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
- G_CALLBACK (plugin_connection_added), self);
- g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
- G_CALLBACK (unmanaged_specs_changed), self);
- g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
- G_CALLBACK (unrecognized_specs_changed), self);
+ reprioritize = FALSE;
+ c_list_for_each_entry (sd, &sett_conn_entry->dirty_sd_lst_head, sd_lst) {
+ if (sd->prioritize) {
+ reprioritize = TRUE;
+ break;
+ }
}
- priv->connections_loaded = TRUE;
- _notify (self, PROP_CONNECTIONS);
+ c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) {
- unmanaged_specs_changed (NULL, self);
- unrecognized_specs_changed (NULL, 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. */
+ if (reprioritize)
+ sd->prioritize = FALSE;
+ continue;
+ }
+
+ nm_g_object_ref_set (&sd->connection, sd_dirty->connection);
+ sd->prioritize = sd_dirty->prioritize;
+
+ _storage_data_destroy (sd_dirty);
+ }
+
+ /* all remaining (so far unseen) dirty entries are appended to the merged list.
+ * (append means lower priority). */
+
+ c_list_splice (&sett_conn_entry->sd_lst_head, &sett_conn_entry->dirty_sd_lst_head);
+
+ /* we drop the entries that are no longer "alive" (meaning, they no longer
+ * indicate a connection and are not a tombstone). */
+ c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) {
+ if (!_storage_data_is_alive (sd))
+ _storage_data_destroy (sd);
+ }
+
+ /* as last, we sort the entries. Note that this is a stable-sort... */
+ c_list_sort (&sett_conn_entry->sd_lst_head,
+ _sett_conn_entry_sds_update_cmp,
+ NM_SETTINGS_GET_PRIVATE (self)->plugins);
+
+ nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head);
+ nm_assert (c_list_is_empty (&sett_conn_entry->dirty_sd_lst_head));
}
/*****************************************************************************/
-static void
-connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer user_data)
+static NMConnection *
+_connection_changed_normalize_connection (NMSettingsStorage *storage,
+ NMConnection *connection,
+ GVariant *secrets_to_merge,
+ NMConnection **out_connection_cloned)
{
- g_signal_emit (NM_SETTINGS (user_data),
- signals[CONNECTION_UPDATED],
- 0,
- connection,
- by_user);
+ gs_unref_object NMConnection *connection_cloned = NULL;
+ gs_free_error GError *error = NULL;
+ const char *uuid;
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+ nm_assert (out_connection_cloned && !*out_connection_cloned);
+
+ if (!connection)
+ return NULL;
+
+ nm_assert (NM_IS_CONNECTION (connection));
+
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ if (secrets_to_merge) {
+ connection_cloned = nm_simple_connection_new_clone (connection);
+ connection = connection_cloned;
+ nm_connection_update_secrets (connection,
+ NULL,
+ secrets_to_merge,
+ NULL);
+ }
+
+ if (!_nm_connection_ensure_normalized (connection,
+ !!connection_cloned,
+ uuid,
+ FALSE,
+ connection_cloned ? NULL : &connection_cloned,
+ &error)) {
+ /* this is most likely a bug in the plugin. It provided a connection that no longer verifies.
+ * Well, I guess it could also happen when we merge @secrets_to_merge above. In any case
+ * somewhere is a bug. */
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: plugin provided an invalid connection: %s",
+ uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ error->message);
+ return NULL;
+ }
+ if (connection_cloned)
+ connection = connection_cloned;
+
+ *out_connection_cloned = g_steal_pointer (&connection_cloned);
+ return connection;
}
+/*****************************************************************************/
+
static void
-connection_flags_changed (NMSettingsConnection *connection,
- gpointer user_data)
+_connection_changed_update (NMSettings *self,
+ SettConnEntry *sett_conn_entry,
+ NMConnection *connection,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason)
{
- g_signal_emit (NM_SETTINGS (user_data),
- signals[CONNECTION_FLAGS_CHANGED],
- 0,
- connection);
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection_old = NULL;
+ NMSettingsStorage *storage = sett_conn_entry->storage;
+ gs_unref_object NMSettingsConnection *sett_conn = g_object_ref (sett_conn_entry->sett_conn);
+ const char *path;
+ gboolean is_new;
+
+ nm_assert (!NM_FLAGS_ANY (sett_mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask));
+
+ is_new = c_list_is_empty (&sett_conn->_connections_lst);
+
+ _LOGT ("update[%s]: %s connection \"%s\" ("NM_SETTINGS_STORAGE_PRINT_FMT")",
+ nm_settings_storage_get_uuid (storage),
+ is_new ? "adding" : "updating",
+ nm_connection_get_id (connection),
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage));
+
+ _nm_settings_connection_set_storage (sett_conn, storage);
+
+ _nm_settings_connection_set_connection (sett_conn, connection, &connection_old, update_reason);
+
+
+ if (is_new) {
+ _nm_settings_connection_register_kf_dbs (sett_conn,
+ priv->kf_db_timestamps,
+ priv->kf_db_seen_bssids);
+
+ _clear_connections_cached_list (priv);
+ c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst);
+ priv->connections_len++;
+ priv->connections_generation++;
+
+ g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self);
+
+ }
+
+ sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE;
+ if (nm_settings_connection_check_visibility (sett_conn, priv->session_monitor))
+ sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE;
+ else
+ 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))
+ sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
+ else {
+ nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED));
+
+ /* Profiles that don't reside in /run, are never nm-generated
+ * and never volatile. */
+ sett_mask |= ( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE);
+ sett_flags &= ~( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE);
+ }
+
+ nm_settings_connection_set_flags_full (sett_conn,
+ sett_mask,
+ sett_flags);
+
+ if (is_new) {
+ /* FIXME(shutdown): The NMSettings instance can't be disposed
+ * while there is any exported connection. Ideally we should
+ * unexport all connections on NMSettings' disposal, but for now
+ * leak @self on termination when there are connections alive. */
+ path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn));
+ } else
+ path = nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn));
+
+ if ( is_new
+ || connection_old) {
+ nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn),
+ connection_old,
+ LOGL_DEBUG,
+ LOGD_CORE,
+ is_new ? "new connection" : "update connection",
+ "++ ",
+ path);
+ }
+
+ if (is_new) {
+ nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+ &interface_info_settings,
+ &signal_info_new_connection,
+ "(o)",
+ path);
+ _notify (self, PROP_CONNECTIONS);
+ _emit_connection_added (self, sett_conn);
+ } else {
+ _nm_settings_connection_emit_dbus_signal_updated (sett_conn);
+ _emit_connection_updated (self, sett_conn, update_reason);
+ }
+
+ if ( !priv->started
+ || priv->startup_complete_idx) {
+ if (nm_settings_has_connection (self, sett_conn))
+ _startup_complete_notify_connection (self, sett_conn, FALSE);
+ }
}
static void
-connection_removed (NMSettingsConnection *connection, gpointer user_data)
+_connection_changed_delete (NMSettings *self,
+ NMSettingsStorage *storage,
+ NMSettingsConnection *sett_conn,
+ gboolean allow_add_to_no_auto_default)
{
- NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection_for_agents = NULL;
NMDevice *device;
+ const char *uuid;
- g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
- g_return_if_fail (!c_list_is_empty (&connection->_connections_lst));
- nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+ nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst));
+ nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn)));
+
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ _LOGT ("update[%s]: delete connection \"%s\" ("NM_SETTINGS_STORAGE_PRINT_FMT")",
+ uuid,
+ nm_settings_connection_get_id (sett_conn),
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage));
- /* When the default wired connection is removed (either deleted or saved to
- * a new persistent connection by a plugin), write the MAC address of the
+ /* When the default wired sett_conn is removed (either deleted or saved to
+ * a new persistent sett_conn by a plugin), write the MAC address of the
* wired device to the config file and don't create a new default wired
- * connection for that device again.
+ * sett_conn for that device again.
*/
- device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
+ device = nm_settings_connection_default_wired_get_device (sett_conn);
if (device)
- default_wired_clear_tag (self, device, connection, TRUE);
+ default_wired_clear_tag (self, device, sett_conn, allow_add_to_no_auto_default);
- /* Disconnect signal handlers, as plugins might still keep references
- * to the connection (and thus the signal handlers would still be live)
- * even after NMSettings has dropped all its references.
- */
-
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self);
- if (!priv->startup_complete)
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
+ g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_flags_changed), self);
- /* Forget about the connection internally */
_clear_connections_cached_list (priv);
+ c_list_unlink (&sett_conn->_connections_lst);
priv->connections_len--;
- c_list_unlink (&connection->_connections_lst);
+ priv->connections_generation++;
- if (priv->connections_loaded) {
- _notify (self, PROP_CONNECTIONS);
+ /* Tell agents to remove secrets for this connection */
+ connection_for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn));
+ nm_connection_clear_secrets (connection_for_agents);
+ nm_agent_manager_delete_secrets (priv->agent_mgr,
+ nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
+ connection_for_agents);
- nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
- &interface_info_settings,
- &signal_info_connection_removed,
- "(o)",
- nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
- }
+ _notify (self, PROP_CONNECTIONS);
+ _nm_settings_connection_emit_dbus_signal_removed (sett_conn);
+ nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+ &interface_info_settings,
+ &signal_info_connection_removed,
+ "(o)",
+ nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)));
- nm_dbus_object_unexport (NM_DBUS_OBJECT (connection));
+ nm_dbus_object_unexport (NM_DBUS_OBJECT (sett_conn));
- if (priv->connections_loaded)
- g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
+ nm_settings_connection_set_flags (sett_conn,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+ FALSE);
- check_startup_complete (self);
+ _emit_connection_removed (self, sett_conn);
- g_object_unref (connection);
+ _nm_settings_connection_cleanup_after_remove (sett_conn);
- g_object_unref (self); /* Balanced by a ref in claim_connection() */
-}
+ nm_key_file_db_remove_key (priv->kf_db_timestamps, uuid);
+ nm_key_file_db_remove_key (priv->kf_db_seen_bssids, uuid);
-/*****************************************************************************/
+ if ( !priv->started
+ || priv->startup_complete_idx)
+ _startup_complete_notify_connection (self, sett_conn, TRUE);
+}
static void
-claim_connection (NMSettings *self, NMSettingsConnection *sett_conn)
+_connection_changed_process_one (NMSettings *self,
+ SettConnEntry *sett_conn_entry,
+ gboolean allow_add_to_no_auto_default,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ gboolean override_sett_flags,
+ NMSettingsConnectionUpdateReason update_reason)
{
- NMSettingsPrivate *priv;
- GError *error = NULL;
- const char *path;
- NMSettingsConnection *existing;
+ StorageData *sd_best;
- g_return_if_fail (NM_IS_SETTINGS (self));
- g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
- g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn)));
+ c_list_unlink (&sett_conn_entry->sce_dirty_lst);
- priv = NM_SETTINGS_GET_PRIVATE (self);
+ _sett_conn_entry_sds_update (self, sett_conn_entry);
- /* prevent duplicates */
- if (!c_list_is_empty (&sett_conn->_connections_lst)) {
- nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst));
+ sd_best = c_list_first_entry (&sett_conn_entry->sd_lst_head, StorageData, sd_lst);;
+
+ if ( !sd_best
+ || !sd_best->connection) {
+ gs_unref_object NMSettingsConnection *sett_conn = NULL;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+
+ if (!sett_conn_entry->sett_conn) {
+
+ if (!sd_best) {
+ _sett_conn_entries_remove_and_destroy (self, sett_conn_entry);
+ return;
+ }
+
+ if (sett_conn_entry->storage != sd_best->storage) {
+ _LOGT ("update[%s]: shadow UUID ("NM_SETTINGS_STORAGE_PRINT_FMT")",
+ sett_conn_entry->uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (sd_best->storage));
+ }
+
+ nm_g_object_ref_set (&sett_conn_entry->storage, sd_best->storage);
+ return;
+ }
+
+ sett_conn = g_steal_pointer (&sett_conn_entry->sett_conn);
+ if (sd_best) {
+ storage = g_object_ref (sd_best->storage);
+ nm_g_object_ref_set (&sett_conn_entry->storage, storage);
+ nm_assert_valid_settings_storage (NULL, storage);
+ } else {
+ storage = g_object_ref (sett_conn_entry->storage);
+ _sett_conn_entries_remove_and_destroy (self, sett_conn_entry);
+ }
+
+ _connection_changed_delete (self, storage, sett_conn, allow_add_to_no_auto_default);
return;
}
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (!nm_connection_normalize (nm_settings_connection_get_connection (sett_conn), NULL, NULL, &error)) {
- _LOGW ("plugin provided invalid connection: %s", error->message);
- g_error_free (error);
- return;
+ if (override_sett_flags) {
+ NMSettingsConnectionIntFlags s_f, s_m;
+
+ nm_settings_storage_load_sett_flags (sd_best->storage, &s_f, &s_m);
+
+ nm_assert (!NM_FLAGS_ANY (s_f, ~s_m));
+
+ sett_mask |= s_m;
+ sett_flags = (sett_flags & ~s_m) | (s_f & s_m);
}
- existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (sett_conn));
- if (existing) {
- /* Cannot add duplicate connections per UUID. Just return without action and
- * log a warning.
- *
- * This means, that plugins must not provide duplicate connections (UUID).
- * In fact, none of the plugins currently would do that.
- *
- * But globaly, over different setting plugins, there could be duplicates
- * without the individual plugins being aware. Don't handle that at all, just
- * error out. That should not happen unless the admin misconfigured the system
- * to create conflicting connections. */
- _LOGW ("plugin provided duplicate connection with UUID %s",
- nm_settings_connection_get_uuid (sett_conn));
- return;
+ nm_g_object_ref_set (&sett_conn_entry->storage, sd_best->storage);
+
+ if (!sett_conn_entry->sett_conn)
+ sett_conn_entry->sett_conn = nm_settings_connection_new ();
+
+ _connection_changed_update (self,
+ sett_conn_entry,
+ sd_best->connection,
+ sett_flags,
+ sett_mask,
+ update_reason);
+}
+
+static void
+_connection_changed_process_all_dirty (NMSettings *self,
+ gboolean allow_add_to_no_auto_default,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ gboolean override_sett_flags,
+ NMSettingsConnectionUpdateReason update_reason)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ SettConnEntry *sett_conn_entry;
+
+ while ((sett_conn_entry = c_list_first_entry (&priv->sce_dirty_lst_head, SettConnEntry, sce_dirty_lst))) {
+ _connection_changed_process_one (self,
+ sett_conn_entry,
+ allow_add_to_no_auto_default,
+ sett_flags,
+ sett_mask,
+ override_sett_flags,
+ update_reason);
}
+}
+
+static SettConnEntry *
+_connection_changed_track (NMSettings *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gboolean prioritize)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ SettConnEntry *sett_conn_entry;
+ StorageData *sd;
+ const char *uuid;
- nm_settings_connection_register_kf_dbs (sett_conn,
- priv->kf_db_timestamps,
- priv->kf_db_seen_bssids);
+ nm_assert_valid_settings_storage (NULL, storage);
- /* Ensure its initial visibility is up-to-date */
- nm_settings_connection_recheck_visibility (sett_conn);
+ uuid = nm_settings_storage_get_uuid (storage);
- /* This one unexports the connection, it needs to run late to give the active
- * connection a chance to deal with its reference to this settings connection. */
- g_signal_connect_after (sett_conn, NM_SETTINGS_CONNECTION_REMOVED,
- G_CALLBACK (connection_removed), self);
- g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
- G_CALLBACK (connection_updated), self);
- g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
- G_CALLBACK (connection_flags_changed),
- self);
- if (!priv->startup_complete) {
- g_signal_connect (sett_conn, "notify::" NM_SETTINGS_CONNECTION_READY,
- G_CALLBACK (connection_ready_changed),
- self);
+ nm_assert (!connection || NM_IS_CONNECTION (connection));
+ nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
+ nm_assert (!connection || nm_streq0 (uuid, nm_connection_get_uuid (connection)));
+
+ nmtst_connection_assert_unchanging (connection);
+
+ sett_conn_entry = _sett_conn_entries_get (self, uuid)
+ ?: _sett_conn_entries_create_and_add (self, uuid);
+
+ if (_LOGT_ENABLED ()) {
+ const char *filename;
+
+ filename = nm_settings_storage_get_filename (storage);
+ if (connection) {
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event with connection \"%s\"%s%s%s",
+ sett_conn_entry->uuid,
+ 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)) {
+ _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),
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+ } else {
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for dropping profile%s%s%s",
+ sett_conn_entry->uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+ }
}
- _clear_connections_cached_list (priv);
+ /* see _sett_conn_entry_sds_update() for why we append the new events
+ * and leave existing ones at their position. */
+ sd = _storage_data_find_in_lst (&sett_conn_entry->dirty_sd_lst_head, storage);
+ if (sd)
+ nm_g_object_ref_set (&sd->connection, connection);
+ else {
+ sd = _storage_data_new_stale (storage, connection);
+ c_list_link_tail (&sett_conn_entry->dirty_sd_lst_head, &sd->sd_lst);
+ }
- g_object_ref (sett_conn);
- /* FIXME(shutdown): The NMSettings instance can't be disposed
- * while there is any exported connection. Ideally we should
- * unexport all connections on NMSettings' disposal, but for now
- * leak @self on termination when there are connections alive. */
- g_object_ref (self);
- priv->connections_len++;
- c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst);
-
- path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn));
-
- nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn),
- NULL,
- LOGL_DEBUG,
- LOGD_CORE,
- "new connection", "++ ",
- path);
-
- /* Only emit the individual connection-added signal after connections
- * have been initially loaded.
- */
- if (priv->connections_loaded) {
- nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
- &interface_info_settings,
- &signal_info_new_connection,
- "(o)",
- nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)));
+ if (prioritize) {
+ StorageData *sd2;
- g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
- _notify (self, PROP_CONNECTIONS);
+ /* only one entry can be prioritized. */
+ c_list_for_each_entry (sd2, &sett_conn_entry->dirty_sd_lst_head, sd_lst)
+ sd2->prioritize = FALSE;
+ sd->prioritize = TRUE;
+ }
+
+ nm_c_list_move_tail (&priv->sce_dirty_lst_head, &sett_conn_entry->sce_dirty_lst);
+
+ return sett_conn_entry;
+}
+
+/*****************************************************************************/
+
+static void
+_plugin_connections_reload_cb (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gpointer user_data)
+{
+ _connection_changed_track (user_data, storage, connection, FALSE);
+}
+
+static void
+_plugin_connections_reload (NMSettings *self)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ nm_settings_plugin_reload_connections (iter->data,
+ _plugin_connections_reload_cb,
+ self);
}
- nm_settings_connection_added (sett_conn);
+ _connection_changed_process_all_dirty (self,
+ FALSE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ TRUE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
}
/*****************************************************************************/
+static gboolean
+_add_connection_to_first_plugin (NMSettings *self,
+ NMConnection *new_connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean in_memory,
+ NMSettingsStorage **out_new_storage,
+ NMConnection **out_new_connection,
+ GError **error)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ GError *first_error = NULL;
+ GSList *iter;
+ const char *uuid;
+
+ uuid = nm_connection_get_uuid (new_connection);
+
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ gs_unref_object NMConnection *connection_to_add = NULL;
+ gs_unref_object NMConnection *connection_to_add_cloned = NULL;
+ NMConnection *connection_to_add_real = NULL;
+ gs_unref_variant GVariant *agent_owned_secrets = NULL;
+ gs_free_error GError *add_error = NULL;
+ gboolean success;
+ const char *filename;
+
+ if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) {
+ success = nms_keyfile_plugin_add_connection (priv->keyfile_plugin,
+ new_connection,
+ is_nm_generated,
+ is_volatile,
+ in_memory,
+ &storage,
+ &connection_to_add,
+ &add_error);
+ } else {
+ if (in_memory)
+ continue;
+ nm_assert (!is_nm_generated);
+ nm_assert (!is_volatile);
+ success = nm_settings_plugin_add_connection (plugin,
+ new_connection,
+ &storage,
+ &connection_to_add,
+ &add_error);
+ }
+ if (!success) {
+ _LOGT ("add-connection: failed to add %s/'%s': %s",
+ nm_connection_get_uuid (new_connection),
+ nm_connection_get_id (new_connection),
+ add_error->message);
+ if (!first_error)
+ first_error = g_steal_pointer (&add_error);
+ continue;
+ }
+
+ if (!nm_streq0 (nm_settings_storage_get_uuid (storage), uuid)) {
+ nm_assert_not_reached ();
+ continue;
+ }
+
+ agent_owned_secrets = nm_connection_to_dbus (new_connection,
+ NM_CONNECTION_SERIALIZE_ONLY_SECRETS
+ | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+ connection_to_add_real = _connection_changed_normalize_connection (storage,
+ connection_to_add,
+ agent_owned_secrets,
+ &connection_to_add_cloned);
+ if (!connection_to_add_real) {
+ nm_assert_not_reached ();
+ continue;
+ }
+
+ filename = nm_settings_storage_get_filename (storage);
+ _LOGT ("add-connection: successfully added connection %s,'%s' ("NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s",
+ nm_settings_storage_get_uuid (storage),
+ nm_connection_get_id (new_connection),
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ NM_PRINT_FMT_QUOTED (filename, ", \"", filename, "\")", ")"));
+
+ *out_new_storage = g_steal_pointer (&storage);
+ *out_new_connection = g_steal_pointer (&connection_to_add_cloned)
+ ?: g_steal_pointer (&connection_to_add);
+ nm_assert (NM_IS_CONNECTION (*out_new_connection));
+ return TRUE;
+ }
+
+ nm_assert (first_error);
+ g_propagate_error (error, first_error);
+ return FALSE;
+}
+
/**
* nm_settings_add_connection:
* @self: the #NMSettings object
* @connection: the source connection to create a new #NMSettingsConnection from
- * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
- * not save to disk
+ * @persist_mode: the persist-mode for this profile.
+ * @sett_flags: the settings flags to set.
+ * @out_sett_conn: (allow-none) (transfer none): the added settings connection on success.
* @error: on return, a location to store any errors that may occur
*
* Creates a new #NMSettingsConnection for the given source @connection.
* The returned object is owned by @self and the caller must reference
* the object to continue using it.
*
- * Returns: the new #NMSettingsConnection or %NULL
+ * Returns: TRUE on success.
*/
-NMSettingsConnection *
+gboolean
nm_settings_add_connection (NMSettings *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnection **out_sett_conn,
GError **error)
{
- NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
- GSList *iter;
- NMSettingsConnection *added = NULL;
- NMSettingsConnection *candidate = NULL;
+ gs_unref_object NMConnection *connection_cloned_1 = NULL;
+ gs_unref_object NMConnection *new_connection = NULL;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ gs_free_error GError *local = NULL;
+ SettConnEntry *sett_conn_entry;
const char *uuid;
+ nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY));
+
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+
+ nm_assert ( !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
+ || persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
+
+ nm_assert ( !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)
+ || persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
+
+ NM_SET_OUT (out_sett_conn, NULL);
+
uuid = nm_connection_get_uuid (connection);
/* Make sure a connection with this UUID doesn't already exist */
- c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
- if (nm_streq0 (uuid, nm_settings_connection_get_uuid (candidate))) {
- g_set_error_literal (error,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_UUID_EXISTS,
- "A connection with this UUID already exists.");
- return NULL;
+ if (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid))) {
+ g_set_error_literal (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_UUID_EXISTS,
+ "a connection with this UUID already exists");
+ return FALSE;
+ }
+
+ if (!_nm_connection_ensure_normalized (connection,
+ FALSE,
+ NULL,
+ FALSE,
+ &connection_cloned_1,
+ &local)) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
+ "connection is invalid: %s",
+ local->message);
+ return FALSE;
+ }
+ if (connection_cloned_1)
+ connection = connection_cloned_1;
+
+ 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,
+ &new_connection,
+ &local)) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_FAILED,
+ "failure adding connection: %s",
+ local->message);
+ return FALSE;
+ }
+
+ sett_conn_entry = _connection_changed_track (self, storage, new_connection, TRUE);
+
+ _connection_changed_process_all_dirty (self,
+ FALSE,
+ sett_flags,
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK,
+ FALSE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+
+ nm_assert (sett_conn_entry == _sett_conn_entries_get (self, sett_conn_entry->uuid));
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn_entry->sett_conn));
+
+ NM_SET_OUT (out_sett_conn, _sett_conn_entry_get_conn (sett_conn_entry));
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_settings_update_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ NMConnection *connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
+ GError **error)
+{
+ NMSettingsPrivate *priv;
+ gs_unref_object NMConnection *connection_cloned_1 = NULL;
+ 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);
+ g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE);
+
+ nm_assert (!NM_FLAGS_ANY (sett_mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask));
+ nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY));
+
+ priv = NM_SETTINGS_GET_PRIVATE (self);
+
+ cur_storage = g_object_ref (nm_settings_connection_get_storage (sett_conn));
+
+ uuid = nm_settings_storage_get_uuid (cur_storage);
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage));
+ nm_assert (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid)) == sett_conn);
+
+ if (connection) {
+ if (!_nm_connection_ensure_normalized (connection,
+ FALSE,
+ uuid,
+ TRUE,
+ &connection_cloned_1,
+ &local)) {
+ _LOGT ("update[%s]: %s: failed because profile is invalid: %s",
+ nm_settings_storage_get_uuid (cur_storage),
+ log_context_name,
+ local->message);
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
+ "connection is invalid: %s",
+ local->message);
+ return FALSE;
}
+ if (connection_cloned_1)
+ connection = connection_cloned_1;
+ } else
+ connection = nm_settings_connection_get_connection (sett_conn);
+
+ cur_in_memory = !!nm_settings_storage_is_in_memory (cur_storage);
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) {
+ persist_mode = cur_in_memory
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
}
- /* 1) plugin writes the NMConnection to disk
- * 2) plugin creates a new NMSettingsConnection subclass with the settings
- * from the NMConnection and returns it to the settings service
- * 3) settings service exports the new NMSettingsConnection subclass
- * 4) plugin notices that something on the filesystem has changed
- * 5) plugin reads the changes and ignores them because they will
- * contain the same data as the connection it already knows about
- */
- for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
- GError *add_error = NULL;
- gs_unref_variant GVariant *secrets = NULL;
-
- /* Make a copy of agent-owned secrets because they won't be present in
- * the connection returned by plugins, as plugins return only what was
- * reread from the file. */
- secrets = nm_connection_to_dbus (connection,
- NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
-
- added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error);
- if (added) {
- if (secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (added),
- NULL,
- secrets,
- NULL);
+ if ( NM_FLAGS_HAS (sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
+ && !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
+ NMDevice *device;
+
+ /* The connection has been changed by the user, it should no longer be
+ * considered a default wired connection, and should no longer affect
+ * the no-auto-default configuration option.
+ */
+ device = nm_settings_connection_default_wired_get_device (sett_conn);
+ if (device) {
+ nm_assert (cur_in_memory);
+ nm_assert (!NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE));
+
+ default_wired_clear_tag (self, device, sett_conn, FALSE);
+
+ if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)) {
+ /* making a default-wired-connection a regulard connection implies persisting
+ * it to disk (unless specified differently). */
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
}
- claim_connection (self, added);
- return added;
}
- _LOGD ("Failed to add %s/'%s': %s",
- nm_connection_get_uuid (connection),
+ }
+
+ 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_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
+ * to store the flags in run. */
+ nm_assert (cur_in_memory);
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+ }
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK)
+ new_in_memory = FALSE;
+ else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY))
+ new_in_memory = TRUE;
+ else {
+ nm_assert (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST);
+ new_in_memory = cur_in_memory;
+ }
+
+ if (!new_in_memory) {
+ /* Persistent connections cannot be volatile nor nm-generated.
+ *
+ * That is obviously true for volatile, as it is enforced by Update2() API.
+ *
+ * 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. */
+ 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
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+ sett_flags &= ~( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | 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);
+ } 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);
+ } 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;
+
+ 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),
- add_error->message);
- g_clear_error (&add_error);
+ 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;
+ }
+ 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)" : "");
}
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "No plugin supported adding this connection");
- return NULL;
+ nm_assert_valid_settings_storage (NULL, new_storage);
+ nm_assert (NM_IS_CONNECTION (new_connection));
+ nm_assert (nm_streq (uuid, nm_settings_storage_get_uuid (new_storage)));
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)
+ new_connection_real = new_connection;
+ else {
+ gs_unref_variant GVariant *agent_owned_secrets = NULL;
+
+ agent_owned_secrets = nm_connection_to_dbus (connection,
+ NM_CONNECTION_SERIALIZE_ONLY_SECRETS
+ | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+ new_connection_real = _connection_changed_normalize_connection (new_storage,
+ new_connection,
+ agent_owned_secrets,
+ &new_connection_cloned);
+ }
+
+ nm_assert (NM_IS_CONNECTION (new_connection_real));
+
+ _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, "\")", ""));
+
+ g_clear_error (&local);
+ }
+
+ _connection_changed_track (self, cur_storage, NULL, FALSE);
+ }
+
+ _connection_changed_process_all_dirty (self,
+ FALSE,
+ sett_flags,
+ sett_mask,
+ FALSE,
+ update_reason);
+
+ return TRUE;
}
+void
+nm_settings_delete_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ gboolean remove_from_disk,
+ gboolean allow_add_to_no_auto_default)
+{
+ NMSettingsStorage *cur_storage;
+ gs_free_error GError *local = NULL;
+ SettConnEntry *sett_conn_entry = NULL;
+ const char *uuid;
+
+ g_return_if_fail (NM_IS_SETTINGS (self));
+ g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
+
+ cur_storage = nm_settings_connection_get_storage (sett_conn);
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage));
+
+ uuid = nm_settings_storage_get_uuid (cur_storage);
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ sett_conn_entry = _sett_conn_entries_get (self, uuid);
+
+ g_return_if_fail (sett_conn_entry);
+ 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... */
+ }
+
+ _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,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ FALSE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE);
+}
+
+/*****************************************************************************/
+
static void
send_agent_owned_secrets (NMSettings *self,
NMSettingsConnection *sett_conn,
@@ -657,7 +1807,6 @@ pk_add_cb (NMAuthChain *chain,
gpointer callback_data;
NMAuthSubject *subject;
const char *perm;
- gboolean save_to_disk;
nm_assert (G_IS_DBUS_METHOD_INVOCATION (context));
@@ -675,10 +1824,14 @@ pk_add_cb (NMAuthChain *chain,
} else {
/* Authorized */
connection = nm_auth_chain_get_data (chain, "connection");
- nm_assert (connection);
+ nm_assert (NM_IS_CONNECTION (connection));
- save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk"));
- added = nm_settings_add_connection (self, connection, save_to_disk, &error);
+ nm_settings_add_connection (self,
+ connection,
+ GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "persist-mode")),
+ GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sett-flags")),
+ &added,
+ &error);
/* The callback may remove the connection from the settings manager (e.g.
* because it's found to be incompatible with the device on AddAndActivate).
@@ -694,8 +1847,7 @@ pk_add_cb (NMAuthChain *chain,
callback (self, added, error, context, subject, callback_data);
/* Send agent-owned secrets to the agents */
- if ( !error
- && added
+ if ( added
&& nm_settings_has_connection (self, added))
send_agent_owned_secrets (self, added, subject);
}
@@ -703,7 +1855,8 @@ pk_add_cb (NMAuthChain *chain,
void
nm_settings_add_connection_dbus (NMSettings *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
NMAuthSubject *subject,
GDBusMethodInvocation *context,
NMSettingsAddCallback callback,
@@ -719,6 +1872,8 @@ nm_settings_add_connection_dbus (NMSettings *self,
g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (context));
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+
/* Connection must be valid, of course */
if (_nm_connection_verify (connection, &tmp_error) != NM_SETTING_VERIFY_SUCCESS) {
error = g_error_new (NM_SETTINGS_ERROR,
@@ -774,7 +1929,8 @@ nm_settings_add_connection_dbus (NMSettings *self,
nm_auth_chain_set_data (chain, "callback", callback, NULL);
nm_auth_chain_set_data (chain, "callback-data", user_data, NULL);
nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref);
- nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL);
+ nm_auth_chain_set_data (chain, "persist-mode", GUINT_TO_POINTER (persist_mode), NULL);
+ nm_auth_chain_set_data (chain, "sett-flags", GUINT_TO_POINTER (sett_flags), NULL);
nm_auth_chain_add_call_unsafe (chain, perm, TRUE);
return;
@@ -808,7 +1964,7 @@ static void
settings_add_connection_helper (NMSettings *self,
GDBusMethodInvocation *context,
GVariant *settings,
- gboolean save_to_disk)
+ NMSettingsConnectionPersistMode persist_mode)
{
gs_unref_object NMConnection *connection = NULL;
GError *error = NULL;
@@ -836,7 +1992,8 @@ settings_add_connection_helper (NMSettings *self,
nm_settings_add_connection_dbus (self,
connection,
- save_to_disk,
+ persist_mode,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
subject,
context,
settings_add_connection_add_cb,
@@ -856,7 +2013,7 @@ impl_settings_add_connection (NMDBusObject *obj,
gs_unref_variant GVariant *settings = NULL;
g_variant_get (parameters, "(@a{sa{sv}})", &settings);
- settings_add_connection_helper (self, invocation, settings, TRUE);
+ settings_add_connection_helper (self, invocation, settings, NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK);
}
static void
@@ -872,14 +2029,16 @@ impl_settings_add_connection_unsaved (NMDBusObject *obj,
gs_unref_variant GVariant *settings = NULL;
g_variant_get (parameters, "(@a{sa{sv}})", &settings);
- settings_add_connection_helper (self, invocation, settings, FALSE);
+ settings_add_connection_helper (self, invocation, settings, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
}
+/*****************************************************************************/
+
static void
impl_settings_load_connections (NMDBusObject *obj,
const NMDBusInterfaceInfoExtended *interface_info,
const NMDBusMethodInfoExtended *method_info,
- GDBusConnection *connection,
+ GDBusConnection *dbus_connection,
const char *sender,
GDBusMethodInvocation *invocation,
GVariant *parameters)
@@ -906,26 +2065,54 @@ impl_settings_load_connections (NMDBusObject *obj,
gsize i;
for (i = 0; filenames[i]; i++) {
- GSList *iter;
+ gboolean handled = FALSE;
if (filenames[i][0] != '/')
_LOGW ("load: connection filename '%s' is not an absolute path", filenames[i]);
else {
- for (iter = priv->plugins; iter; iter = iter->next) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+ GSList *iter;
- if (nm_settings_plugin_load_connection (plugin, filenames[i]))
- goto next_filename;
+ 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;
}
}
- if (!failures)
- failures = g_ptr_array_new ();
- g_ptr_array_add (failures, (char *) filenames[i]);
-
-next_filename:
- ;
+ if (!handled) {
+ if (!failures)
+ failures = g_ptr_array_new ();
+ g_ptr_array_add (failures, (char *) filenames[i]);
+ }
}
+
+ _connection_changed_process_all_dirty (self,
+ TRUE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ TRUE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
}
if (failures)
@@ -937,6 +2124,8 @@ next_filename:
failures
? (const char **) failures->pdata
: NM_PTRARRAY_EMPTY (const char *)));
+
+ /* FIXME: add audit-logging. */
}
static void
@@ -949,8 +2138,6 @@ impl_settings_reload_connections (NMDBusObject *obj,
GVariant *parameters)
{
NMSettings *self = NM_SETTINGS (obj);
- NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
- GSList *iter;
/* The permission is already enforced by the D-Bus daemon, but we ensure
* that the caller is still alive so that clients are forced to wait and
@@ -963,13 +2150,11 @@ impl_settings_reload_connections (NMDBusObject *obj,
NM_SETTINGS_ERROR_PERMISSION_DENIED))
return;
- for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
-
- nm_settings_plugin_reload_connections (plugin);
- }
+ _plugin_connections_reload (self);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
+
+ /* FIXME: add audit-logging. */
}
/*****************************************************************************/
@@ -1019,20 +2204,24 @@ impl_settings_list_connections (NMDBusObject *obj,
NMSettingsConnection *
nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
{
- NMSettingsPrivate *priv;
- NMSettingsConnection *candidate;
-
g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
g_return_val_if_fail (uuid != NULL, NULL);
- priv = NM_SETTINGS_GET_PRIVATE (self);
+ return _sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid));
+}
- c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
- if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate)))
- return candidate;
- }
+const char *
+nm_settings_get_dbus_path_for_uuid (NMSettings *self,
+ const char *uuid)
+{
+ NMSettingsConnection *sett_conn;
- return NULL;
+ sett_conn = nm_settings_get_connection_by_uuid (self, uuid);
+
+ if (!sett_conn)
+ return NULL;
+
+ return nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn));
}
static void
@@ -1249,6 +2438,9 @@ add_plugin (NMSettings *self,
nm_assert (NM_IS_SETTINGS (self));
nm_assert (NM_IS_SETTINGS_PLUGIN (plugin));
+ nm_assert (pname);
+ nm_assert (nm_streq0 (pname, nm_settings_plugin_get_plugin_name (plugin)));
+
priv = NM_SETTINGS_GET_PRIVATE (self);
nm_assert (!g_slist_find (priv->plugins, plugin));
@@ -1504,6 +2696,13 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
if (!nm_device_check_connection_compatible (device, connection, NULL))
continue;
+ if (nm_settings_connection_default_wired_get_device (sett_conn))
+ continue;
+
+ if (NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
+ continue;
+
iface = nm_setting_connection_get_interface_name (s_con);
if (nm_streq0 (iface, nm_device_get_iface (device)))
continue;
@@ -1535,38 +2734,20 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
}
static void
-default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, gboolean by_user, NMSettings *self)
-{
- NMDevice *device;
-
- if (!by_user)
- return;
-
- /* The connection has been changed by the user, it should no longer be
- * considered a default wired connection, and should no longer affect
- * the no-auto-default configuration option.
- */
- device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
- if (device)
- default_wired_clear_tag (self, device, connection, FALSE);
-}
-
-static void
default_wired_clear_tag (NMSettings *self,
NMDevice *device,
- NMSettingsConnection *connection,
+ NMSettingsConnection *sett_conn,
gboolean add_to_no_auto_default)
{
nm_assert (NM_IS_SETTINGS (self));
nm_assert (NM_IS_DEVICE (device));
- nm_assert (NM_IS_SETTINGS_CONNECTION (connection));
- nm_assert (device == g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()));
- nm_assert (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+ nm_assert (device == nm_settings_connection_default_wired_get_device (sett_conn));
+ nm_assert (sett_conn == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
- g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
- g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
+ nm_settings_connection_default_wired_set_device (sett_conn, NULL);
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
+ g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
if (add_to_no_auto_default)
nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device);
@@ -1575,7 +2756,7 @@ default_wired_clear_tag (NMSettings *self,
static void
device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingsConnection *added;
GError *error = NULL;
@@ -1598,10 +2779,12 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
if (!connection)
return;
- /* Add the connection */
- added = nm_settings_add_connection (self, connection, FALSE, &error);
- g_object_unref (connection);
-
+ nm_settings_add_connection (self,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error);
if (!added) {
if (!g_error_matches (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS)) {
_LOGW ("(%s) couldn't create default wired connection: %s",
@@ -1612,11 +2795,9 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
return;
}
- g_object_set_qdata (G_OBJECT (added), _default_wired_device_quark (), device);
- g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added);
+ nm_settings_connection_default_wired_set_device (added, device);
- g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
- G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
+ g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added);
_LOGI ("(%s): created default wired connection '%s'",
nm_device_get_iface (device),
@@ -1629,6 +2810,8 @@ nm_settings_device_added (NMSettings *self, NMDevice *device)
if (nm_device_is_real (device))
device_realized (device, NULL, self);
else {
+ /* FIXME(shutdown): we need to disconnect this signal handler during
+ * shutdown. */
g_signal_connect_after (device, "notify::" NM_DEVICE_REAL,
G_CALLBACK (device_realized),
self);
@@ -1652,7 +2835,43 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
* remains up and can be assumed if NM starts again.
*/
if (quitting == FALSE)
- nm_settings_connection_delete (connection, NULL);
+ nm_settings_connection_delete (connection, TRUE);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+session_monitor_changed_cb (NMSessionMonitor *session_monitor,
+ NMSettings *self)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ NMSettingsConnection *const*list;
+ guint i, len;
+ guint generation;
+
+again:
+ list = nm_settings_get_connections (self, &len);
+ generation = priv->connections_generation;
+ for (i = 0; i < len; i++) {
+ gboolean is_visible;
+
+ is_visible = nm_settings_connection_check_visibility (list[i],
+ session_monitor);
+ nm_settings_connection_set_flags (list[i],
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,
+ is_visible);
+ if (generation != priv->connections_generation) {
+ /* the cached list was invalidated. Start again.
+ *
+ * Note that nm_settings_connection_recheck_visibility() will do nothing
+ * if the visibility didn't change (including emitting no signals,
+ * and not invalidating the list).
+ *
+ * Hence, for this to be an endless loop, the settings would have
+ * to constantly change the visibility flag and also invalidate the list. */
+ goto again;
+ }
}
}
@@ -1785,9 +3004,14 @@ nm_settings_start (NMSettings *self, GError **error)
{
NMSettingsPrivate *priv;
gs_strfreev char **plugins = NULL;
+ GSList *iter;
priv = NM_SETTINGS_GET_PRIVATE (self);
+ nm_assert (!priv->started);
+
+ priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
+
priv->kf_db_timestamps = nm_key_file_db_new (NMSTATEDIR "/timestamps",
"timestamps",
_kf_db_log_fcn,
@@ -1807,11 +3031,20 @@ nm_settings_start (NMSettings *self, GError **error)
if (!load_plugins (self, (const char *const*) plugins, error))
return FALSE;
- load_connections (self);
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+
+ g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
+ G_CALLBACK (_plugin_unmanaged_specs_changed), self);
+ g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
+ G_CALLBACK (_plugin_unrecognized_specs_changed), self);
+ }
- check_startup_complete (self);
+ _plugin_unmanaged_specs_changed (NULL, self);
+ _plugin_unrecognized_specs_changed (NULL, self);
+
+ _plugin_connections_reload (self);
- priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
g_signal_connect (priv->hostname_manager,
"notify::"NM_HOSTNAME_MANAGER_HOSTNAME,
G_CALLBACK (_hostname_changed_cb),
@@ -1819,6 +3052,14 @@ nm_settings_start (NMSettings *self, GError **error)
if (nm_hostname_manager_get_hostname (priv->hostname_manager))
_notify (self, PROP_HOSTNAME);
+ priv->started = TRUE;
+ _startup_complete_check (self, 0);
+
+ /* FIXME(shutdown): we also need a nm_settings_stop() during shutdown.
+ *
+ * In particular, we need to remove all in-memory keyfiles from /run that are nm-generated.
+ * alternatively, the nm-generated flag must also be persisted and loaded to /run. */
+
return TRUE;
}
@@ -1848,14 +3089,11 @@ get_property (GObject *object, guint prop_id,
g_value_set_boolean (value, TRUE);
break;
case PROP_CONNECTIONS:
- if (priv->connections_loaded) {
- strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
- priv->connections_len,
- G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
- TRUE);
- g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
- } else
- g_value_set_boxed (value, NULL);
+ strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
+ priv->connections_len,
+ G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
+ TRUE);
+ g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
break;
case PROP_STARTUP_COMPLETE:
g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self));
@@ -1876,8 +3114,21 @@ nm_settings_init (NMSettings *self)
c_list_init (&priv->auth_lst_head);
c_list_init (&priv->connections_lst_head);
- priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+ c_list_init (&priv->sce_dirty_lst_head);
+ priv->sce_idx = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal,
+ NULL, (GDestroyNotify) _sett_conn_entry_free);
+
priv->config = g_object_ref (nm_config_get ());
+
+ priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+
+ priv->platform = g_object_ref (NM_PLATFORM_GET);
+
+ priv->session_monitor = g_object_ref (nm_session_monitor_get ());
+ g_signal_connect (priv->session_monitor,
+ NM_SESSION_MONITOR_CHANGED,
+ G_CALLBACK (session_monitor_changed_cb),
+ self);
}
NMSettings *
@@ -1893,6 +3144,12 @@ dispose (GObject *object)
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
CList *iter;
+ nm_assert (c_list_is_empty (&priv->sce_dirty_lst_head));
+ nm_assert (g_hash_table_size (priv->sce_idx) == 0);
+
+ nm_clear_g_source (&priv->startup_complete_timeout_id);
+ nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id);
+ nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
g_clear_object (&priv->startup_complete_blocked_by);
while ((iter = c_list_first (&priv->auth_lst_head)))
@@ -1905,6 +3162,13 @@ dispose (GObject *object)
g_clear_object (&priv->hostname_manager);
}
+ if (priv->session_monitor) {
+ g_signal_handlers_disconnect_by_func (priv->session_monitor,
+ G_CALLBACK (session_monitor_changed_cb),
+ self);
+ g_clear_object (&priv->session_monitor);
+ }
+
G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object);
}
@@ -1919,6 +3183,11 @@ finalize (GObject *object)
nm_assert (c_list_is_empty (&priv->connections_lst_head));
+ nm_assert (c_list_is_empty (&priv->sce_dirty_lst_head));
+ nm_assert (g_hash_table_size (priv->sce_idx) == 0);
+
+ nm_clear_pointer (&priv->sce_idx, g_hash_table_destroy);
+
g_slist_free_full (priv->unmanaged_specs, g_free);
g_slist_free_full (priv->unrecognized_specs, g_free);
@@ -1933,8 +3202,6 @@ finalize (GObject *object)
g_clear_object (&priv->agent_mgr);
- g_clear_object (&priv->config);
-
nm_clear_g_source (&priv->kf_db_flush_idle_id_timestamps);
nm_clear_g_source (&priv->kf_db_flush_idle_id_seen_bssids);
nm_key_file_db_to_file (priv->kf_db_timestamps, FALSE);
@@ -1943,6 +3210,10 @@ finalize (GObject *object)
nm_key_file_db_destroy (priv->kf_db_seen_bssids);
G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
+
+ g_clear_object (&priv->config);
+
+ g_clear_object (&priv->platform);
}
static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
@@ -2113,7 +3384,7 @@ nm_settings_class_init (NMSettingsClass *class)
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
NULL,
- G_TYPE_NONE, 2, NM_TYPE_SETTINGS_CONNECTION, G_TYPE_BOOLEAN);
+ G_TYPE_NONE, 2, NM_TYPE_SETTINGS_CONNECTION, G_TYPE_UINT);
signals[CONNECTION_REMOVED] =
g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 02d65377e0..737e917908 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -27,6 +27,8 @@
#include "nm-connection.h"
+#include "nm-settings-connection.h"
+
#define NM_TYPE_SETTINGS (nm_settings_get_type ())
#define NM_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings))
#define NM_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS, NMSettingsClass))
@@ -67,6 +69,7 @@ NMSettings *nm_settings_get (void);
#define NM_SETTINGS_GET (nm_settings_get ())
NMSettings *nm_settings_new (void);
+
gboolean nm_settings_start (NMSettings *self, GError **error);
typedef void (*NMSettingsAddCallback) (NMSettings *settings,
@@ -78,7 +81,8 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings,
void nm_settings_add_connection_dbus (NMSettings *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
NMAuthSubject *subject,
GDBusMethodInvocation *context,
NMSettingsAddCallback callback,
@@ -93,16 +97,37 @@ NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self,
GCompareDataFunc sort_compare_func,
gpointer sort_data);
-NMSettingsConnection *nm_settings_add_connection (NMSettings *settings,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error);
+gboolean nm_settings_add_connection (NMSettings *settings,
+ NMConnection *connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnection **out_sett_conn,
+ GError **error);
+
+gboolean nm_settings_update_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ NMConnection *new_connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
+ GError **error);
+
+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,
const char *path);
NMSettingsConnection *nm_settings_get_connection_by_uuid (NMSettings *settings,
const char *uuid);
+const char *nm_settings_get_dbus_path_for_uuid (NMSettings *self,
+ const char *uuid);
+
gboolean nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection);
const GSList *nm_settings_get_unmanaged_specs (NMSettings *self);
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
index a238db940e..5f8268e7a4 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-connection.c', 'nms-ifcfg-rh-plugin.c')
+sources = [dbus_sources] + core_sources + files('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-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
deleted file mode 100644
index 5caa861de9..0000000000
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ /dev/null
@@ -1,400 +0,0 @@
-/* NetworkManager system settings service
- *
- * 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) 2008 - 2011 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nms-ifcfg-rh-connection.h"
-
-#include <sys/inotify.h>
-#include <glib/gstdio.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-setting-connection.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-gsm.h"
-#include "nm-setting-cdma.h"
-#include "nm-setting-pppoe.h"
-#include "nm-setting-wireless-security.h"
-#include "nm-setting-8021x.h"
-#include "platform/nm-platform.h"
-#include "nm-config.h"
-
-#include "nms-ifcfg-rh-common.h"
-#include "nms-ifcfg-rh-reader.h"
-#include "nms-ifcfg-rh-writer.h"
-#include "nms-ifcfg-rh-utils.h"
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
- PROP_UNMANAGED_SPEC,
- PROP_UNRECOGNIZED_SPEC,
-);
-
-typedef struct {
- char *unmanaged_spec;
- char *unrecognized_spec;
-
- gulong devtimeout_link_changed_handler;
- guint devtimeout_timeout_id;
-} NMIfcfgConnectionPrivate;
-
-struct _NMIfcfgConnection {
- NMSettingsConnection parent;
- NMIfcfgConnectionPrivate _priv;
-};
-
-struct _NMIfcfgConnectionClass {
- NMSettingsConnectionClass parent;
-};
-
-G_DEFINE_TYPE (NMIfcfgConnection, nm_ifcfg_connection, NM_TYPE_SETTINGS_CONNECTION)
-
-#define NM_IFCFG_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfcfgConnection, NM_IS_IFCFG_CONNECTION)
-
-/*****************************************************************************/
-
-static gboolean
-devtimeout_ready (gpointer user_data)
-{
- NMIfcfgConnection *self = user_data;
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
-
- priv->devtimeout_timeout_id = 0;
- nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE);
- return FALSE;
-}
-
-static void
-link_changed (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformLink *link,
- int change_type_i,
- NMConnection *self)
-{
- const NMPlatformSignalChangeType change_type = change_type_i;
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) self);
- const char *ifname;
-
- ifname = nm_connection_get_interface_name (self);
- if (g_strcmp0 (link->name, ifname) != 0)
- return;
-
- if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
- return;
-
- nm_log_info (LOGD_SETTINGS, "Device %s appeared; connection '%s' now ready",
- ifname, nm_connection_get_id (self));
-
- g_signal_handler_disconnect (platform, priv->devtimeout_link_changed_handler);
- priv->devtimeout_link_changed_handler = 0;
- g_source_remove (priv->devtimeout_timeout_id);
-
- /* Don't declare the connection ready right away, since NMManager may not have
- * started processing the device yet.
- */
- priv->devtimeout_timeout_id = g_idle_add (devtimeout_ready, self);
-}
-
-static gboolean
-devtimeout_expired (gpointer user_data)
-{
- NMIfcfgConnection *self = user_data;
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
-
- nm_log_info (LOGD_SETTINGS, "Device for connection '%s' did not appear before timeout",
- nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self)));
-
- g_signal_handler_disconnect (NM_PLATFORM_GET, priv->devtimeout_link_changed_handler);
- priv->devtimeout_link_changed_handler = 0;
- priv->devtimeout_timeout_id = 0;
-
- nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE);
- return FALSE;
-}
-
-static void
-nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
- NMSettingConnection *s_con;
- const char *ifname;
- const char *filename;
- guint devtimeout;
- const NMPlatformLink *pllink;
-
- s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (self)));
-
- if (!nm_setting_connection_get_autoconnect (s_con))
- return;
- ifname = nm_setting_connection_get_interface_name (s_con);
- if (!ifname)
- return;
- filename = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self));
- if (!filename)
- return;
-
- pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname);
- if (pllink && pllink->initialized)
- return;
-
- devtimeout = devtimeout_from_file (filename);
- if (!devtimeout)
- return;
-
- /* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */
- nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), FALSE);
-
- nm_log_info (LOGD_SETTINGS, "Waiting %u seconds for %s to appear for connection '%s'",
- devtimeout, ifname, nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self)));
-
- priv->devtimeout_link_changed_handler =
- g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED,
- G_CALLBACK (link_changed), self);
- priv->devtimeout_timeout_id = g_timeout_add_seconds (devtimeout, devtimeout_expired, self);
-}
-
-const char *
-nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self)
-{
- g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL);
-
- return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unmanaged_spec;
-}
-
-const char *
-nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self)
-{
- g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL);
-
- return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unrecognized_spec;
-}
-
-static gboolean
-commit_changes (NMSettingsConnection *connection,
- NMConnection *new_connection,
- NMSettingsConnectionCommitReason commit_reason,
- NMConnection **out_reread_connection,
- char **out_logmsg_change,
- GError **error)
-{
- const char *filename;
- gs_unref_object NMConnection *reread = NULL;
- gboolean reread_same = TRUE;
- const char *operation_message;
- gs_free char *ifcfg_path = NULL;
-
- nm_assert (out_reread_connection && !*out_reread_connection);
- nm_assert (!out_logmsg_change || !*out_logmsg_change);
-
- filename = nm_settings_connection_get_filename (connection);
- if (!nms_ifcfg_rh_writer_write_connection (new_connection,
- IFCFG_DIR,
- filename,
- NULL,
- NULL,
- &ifcfg_path,
- &reread,
- &reread_same,
- error))
- return FALSE;
-
- nm_assert ((!filename && ifcfg_path) || (filename && !ifcfg_path));
- if (ifcfg_path) {
- nm_settings_connection_set_filename (connection, ifcfg_path);
- operation_message = "persist";
- } else
- operation_message = "update";
-
- if (reread && !reread_same)
- *out_reread_connection = g_steal_pointer (&reread);
-
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("ifcfg-rh: %s %s",
- operation_message, filename));
- return TRUE;
-}
-
-static gboolean
-delete (NMSettingsConnection *connection,
- GError **error)
-{
- const char *filename;
-
- filename = nm_settings_connection_get_filename (connection);
- if (filename) {
- gs_free char *keyfile = utils_get_keys_path (filename);
- gs_free char *routefile = utils_get_route_path (filename);
- gs_free char *route6file = utils_get_route6_path (filename);
-
- g_unlink (filename);
- if (keyfile)
- g_unlink (keyfile);
- if (routefile)
- g_unlink (routefile);
- if (route6file)
- g_unlink (route6file);
- }
-
- return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object);
-
- switch (prop_id) {
- case PROP_UNMANAGED_SPEC:
- g_value_set_string (value, priv->unmanaged_spec);
- break;
- case PROP_UNRECOGNIZED_SPEC:
- g_value_set_string (value, priv->unrecognized_spec);
- 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)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object);
-
- switch (prop_id) {
- case PROP_UNMANAGED_SPEC:
- priv->unmanaged_spec = g_value_dup_string (value);
- break;
- case PROP_UNRECOGNIZED_SPEC:
- priv->unrecognized_spec = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-/*****************************************************************************/
-
-static void
-nm_ifcfg_connection_init (NMIfcfgConnection *self)
-{
-}
-
-NMIfcfgConnection *
-nm_ifcfg_connection_new (NMConnection *source,
- const char *full_path,
- GError **error,
- gboolean *out_ignore_error)
-{
- GObject *object;
- NMConnection *tmp;
- char *unhandled_spec = NULL;
- const char *unmanaged_spec = NULL, *unrecognized_spec = NULL;
-
- g_assert (source || full_path);
-
- if (out_ignore_error)
- *out_ignore_error = FALSE;
-
- /* If we're given a connection already, prefer that instead of re-reading */
- if (source)
- tmp = g_object_ref (source);
- else {
- tmp = connection_from_file (full_path,
- &unhandled_spec,
- error,
- out_ignore_error);
- if (!tmp)
- return NULL;
- }
-
- if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unmanaged:"))
- unmanaged_spec = unhandled_spec + strlen ("unmanaged:");
- else if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unrecognized:"))
- unrecognized_spec = unhandled_spec + strlen ("unrecognized:");
-
- object = (GObject *) g_object_new (NM_TYPE_IFCFG_CONNECTION,
- NM_SETTINGS_CONNECTION_FILENAME, full_path,
- NM_IFCFG_CONNECTION_UNMANAGED_SPEC, unmanaged_spec,
- NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, unrecognized_spec,
- NULL);
- /* Update our settings with what was read from the file */
- if (nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
- tmp,
- full_path
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- NULL,
- error))
- nm_ifcfg_connection_check_devtimeout (NM_IFCFG_CONNECTION (object));
- else
- g_clear_object (&object);
-
- g_object_unref (tmp);
- g_free (unhandled_spec);
- return (NMIfcfgConnection *) object;
-}
-
-static void
-dispose (GObject *object)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object);
-
- nm_clear_g_signal_handler (NM_PLATFORM_GET, &priv->devtimeout_link_changed_handler);
- nm_clear_g_source (&priv->devtimeout_timeout_id);
-
- g_clear_pointer (&priv->unmanaged_spec, g_free);
- g_clear_pointer (&priv->unrecognized_spec, g_free);
-
- G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->dispose (object);
-}
-
-static void
-nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (ifcfg_connection_class);
- NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifcfg_connection_class);
-
- object_class->set_property = set_property;
- object_class->get_property = get_property;
- object_class->dispose = dispose;
-
- settings_class->delete = delete;
- settings_class->commit_changes = commit_changes;
-
- obj_properties[PROP_UNMANAGED_SPEC] =
- g_param_spec_string (NM_IFCFG_CONNECTION_UNMANAGED_SPEC, "", "",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_UNRECOGNIZED_SPEC] =
- g_param_spec_string (NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, "", "",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h
deleted file mode 100644
index 8f5e77ef33..0000000000
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* NetworkManager system settings service
- *
- * 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) 2008 - 2011 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_IFCFG_CONNECTION_H__
-#define __NETWORKMANAGER_IFCFG_CONNECTION_H__
-
-#include "nm-dbus-interface.h"
-#include "settings/nm-settings-connection.h"
-
-#define NM_TYPE_IFCFG_CONNECTION (nm_ifcfg_connection_get_type ())
-#define NM_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnection))
-#define NM_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass))
-#define NM_IS_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFCFG_CONNECTION))
-#define NM_IS_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFCFG_CONNECTION))
-#define NM_IFCFG_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass))
-
-#define NM_IFCFG_CONNECTION_UNMANAGED_SPEC "unmanaged-spec"
-#define NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC "unrecognized-spec"
-
-typedef struct _NMIfcfgConnection NMIfcfgConnection;
-typedef struct _NMIfcfgConnectionClass NMIfcfgConnectionClass;
-
-GType nm_ifcfg_connection_get_type (void);
-
-NMIfcfgConnection *nm_ifcfg_connection_new (NMConnection *source,
- const char *full_path,
- GError **error,
- gboolean *out_ignore_error);
-
-const char *nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self);
-const char *nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self);
-
-gboolean nm_ifcfg_connection_update (NMIfcfgConnection *self,
- GHashTable *new_settings,
- GError **error);
-
-#endif /* __NETWORKMANAGER_IFCFG_CONNECTION_H__ */
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 6375b6cc14..b260b3f114 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -24,23 +24,24 @@
#include "nms-ifcfg-rh-plugin.h"
-#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <gmodule.h>
+#include <unistd.h>
+#include "nm-std-aux/c-list-util.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-glib-aux/nm-io-utils.h"
#include "nm-std-aux/nm-dbus-compat.h"
-#include "nm-setting-connection.h"
-#include "settings/nm-settings-plugin.h"
+#include "nm-utils.h"
+#include "nm-core-internal.h"
#include "nm-config.h"
+#include "settings/nm-settings-plugin.h"
#include "NetworkManagerUtils.h"
-#include "nms-ifcfg-rh-connection.h"
#include "nms-ifcfg-rh-common.h"
+#include "nms-ifcfg-rh-utils.h"
#include "nms-ifcfg-rh-reader.h"
#include "nms-ifcfg-rh-writer.h"
-#include "nms-ifcfg-rh-utils.h"
-#include "shvar.h"
#define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1"
#define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1"
@@ -49,6 +50,38 @@
/*****************************************************************************/
+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;
+
+ CList sd_lst;
+
+ NMSettingsStorage *storage;
+ NMConnection *connection;
+
+ char *unmanaged_spec;
+ char *unrecognized_spec;
+
+ char filename_data[];
+} StorageData;
+
+typedef struct {
+ CList sd_lst_head;
+ GHashTable *uuid_idx;
+ GHashTable *filename_idx;
+} Storages;
+
typedef struct {
NMConfig *config;
@@ -59,23 +92,25 @@ typedef struct {
guint regist_id;
} dbus;
- GHashTable *connections; /* uuid::connection */
+ Storages storages;
+
+ GHashTable *unmanaged_specs;
+ GHashTable *unrecognized_specs;
- bool initialized:1;
-} SettingsPluginIfcfgPrivate;
+} NMSIfcfgRHPluginPrivate;
-struct _SettingsPluginIfcfg {
+struct _NMSIfcfgRHPlugin {
NMSettingsPlugin parent;
- SettingsPluginIfcfgPrivate _priv;
+ NMSIfcfgRHPluginPrivate _priv;
};
-struct _SettingsPluginIfcfgClass {
+struct _NMSIfcfgRHPluginClass {
NMSettingsPluginClass parent;
};
-G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUGIN)
+G_DEFINE_TYPE (NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN)
-#define SETTINGS_PLUGIN_IFCFG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfcfg, SETTINGS_IS_PLUGIN_IFCFG)
+#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin)
/*****************************************************************************/
@@ -90,531 +125,1238 @@ G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUG
/*****************************************************************************/
-static NMIfcfgConnection *update_connection (SettingsPluginIfcfg *plugin,
- NMConnection *source,
- const char *full_path,
- NMIfcfgConnection *connection,
- gboolean protect_existing_connection,
- GHashTable *protected_connections,
- GError **error);
+static void _unhandled_specs_reset (NMSIfcfgRHPlugin *self);
+
+static void _unhandled_specs_merge_read_data (NMSIfcfgRHPlugin *self,
+ CList *rd_lst_head);
/*****************************************************************************/
-static void
-connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
+static gboolean
+_connection_equals (NMConnection *a,
+ NMConnection *b)
+{
+ return nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT);
+}
+
+static gboolean
+_parse_unhandled_spec (const char *unhandled_spec,
+ char **out_unmanaged_spec,
+ char **out_unrecognized_spec)
{
- g_hash_table_remove (SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) user_data)->connections,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (obj)));
+ nm_assert (unhandled_spec);
+ nm_assert (out_unmanaged_spec && !*out_unmanaged_spec);
+ nm_assert (out_unrecognized_spec && !*out_unrecognized_spec);
+
+ if (NM_STR_HAS_PREFIX (unhandled_spec, "unmanaged:"))
+ *out_unmanaged_spec = g_strdup (&unhandled_spec[NM_STRLEN ("unmanaged:")]);
+ if (NM_STR_HAS_PREFIX (unhandled_spec, "unrecognized:"))
+ *out_unrecognized_spec = g_strdup (&unhandled_spec[NM_STRLEN ("unrecognized:")]);
+ else {
+ nm_assert_not_reached ();
+ return FALSE;
+ }
+
+ return TRUE;
}
+/*****************************************************************************/
+
static void
-remove_connection (SettingsPluginIfcfg *self, NMIfcfgConnection *connection)
+_read_data_destroy (ReadData *rd)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
- gboolean unmanaged, unrecognized;
+ 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_return_if_fail (self != NULL);
- g_return_if_fail (connection != NULL);
+ g_slice_free (ReadData, rd);
+}
- _LOGI ("remove "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection));
+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);
- unmanaged = !!nm_ifcfg_connection_get_unmanaged_spec (connection);
- unrecognized = !!nm_ifcfg_connection_get_unrecognized_spec (connection);
+ 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;
+}
- g_object_ref (connection);
- g_hash_table_remove (priv->connections, nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection)));
- if (!unmanaged && !unrecognized)
- nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
- g_object_unref (connection);
+/*****************************************************************************/
- /* Emit changes _after_ removing the connection */
- if (unmanaged)
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
- if (unrecognized)
- _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+static StorageData *
+_storage_data_new (Storages *storages,
+ const char *filename)
+{
+ StorageData *sd;
+ gsize l = strlen (filename) + 1;
+
+ sd = g_malloc (sizeof (StorageData) + l);
+ sd->filename = sd->filename_data;
+ memcpy (sd->filename_data, filename, l);
+ c_list_init (&sd->sd_lst);
+ sd->unmanaged_spec = NULL;
+ sd->unrecognized_spec = NULL;
+ sd->connection = NULL;
+ sd->storage = NULL;
+
+ c_list_link_tail (&storages->sd_lst_head, &sd->sd_lst);
+ if (!g_hash_table_add (storages->filename_idx, sd))
+ nm_assert_not_reached ();
+
+ return sd;
}
-static NMIfcfgConnection *
-find_by_path (SettingsPluginIfcfg *self, const char *path)
+static void
+_storage_data_destroy (StorageData *sd)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
- GHashTableIter iter;
- NMSettingsConnection *candidate = NULL;
+ 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);
- g_return_val_if_fail (path != NULL, NULL);
+ g_free (sd);
+}
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
- if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0)
- return NM_IFCFG_CONNECTION (candidate);
- }
- return NULL;
+static StorageData *
+_storages_by_filename (Storages *storages,
+ const char *filename)
+{
+ return g_hash_table_lookup (storages->filename_idx, &filename);
}
-static NMIfcfgConnection *
-update_connection (SettingsPluginIfcfg *self,
- NMConnection *source,
- const char *full_path,
- NMIfcfgConnection *connection,
- gboolean protect_existing_connection,
- GHashTable *protected_connections,
- GError **error)
+static StorageData *
+_storages_by_uuid (Storages *storages,
+ const char *uuid)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
- NMIfcfgConnection *connection_new;
- NMIfcfgConnection *connection_by_uuid;
- GError *local = NULL;
- const char *new_unmanaged = NULL, *old_unmanaged = NULL;
- const char *new_unrecognized = NULL, *old_unrecognized = NULL;
- gboolean unmanaged_changed = FALSE, unrecognized_changed = FALSE;
- const char *uuid;
- gboolean ignore_error = FALSE;
-
- g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL);
- g_return_val_if_fail (full_path || source, NULL);
-
- if (full_path)
- _LOGD ("loading from file \"%s\"...", full_path);
-
- /* Create a NMIfcfgConnection instance, either by reading from @full_path or
- * based on @source. */
- connection_new = nm_ifcfg_connection_new (source, full_path, &local, &ignore_error);
- if (!connection_new) {
- /* Unexpected failure. Probably the file is invalid? */
- if ( connection
- && !protect_existing_connection
- && (!protected_connections || !g_hash_table_contains (protected_connections, connection)))
- remove_connection (self, connection);
- if (!source) {
- _NMLOG (ignore_error ? LOGL_DEBUG : LOGL_WARN,
- "loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)");
- }
- g_propagate_error (error, local);
- return NULL;
- }
+ 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
+nm_assert_self (NMSIfcfgRHPlugin *self)
+{
+ nm_assert (NMS_IS_IFCFG_RH_PLUGIN (self));
+
+#if NM_MORE_ASSERTS > 5
+ {
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ StorageData *sd_f;
+ StorageData *sd_u;
+ gsize n_uuid;
+ gs_unref_hashtable GHashTable *h_unmanaged = NULL;
+ gs_unref_hashtable GHashTable *h_unrecognized = NULL;
+
+ nm_assert (g_hash_table_size (priv->storages.filename_idx) == c_list_length (&priv->storages.sd_lst_head));
+
+ h_unmanaged = g_hash_table_new (nm_str_hash, g_str_equal);
+ h_unrecognized = g_hash_table_new (nm_str_hash, g_str_equal);
+
+ n_uuid = 0;
+
+ c_list_for_each_entry (sd_f, &priv->storages.sd_lst_head, sd_lst) {
+
+ nm_assert (!sd_f->unmanaged_spec || strlen (sd_f->unmanaged_spec) > 0);
+ nm_assert (!sd_f->unrecognized_spec || strlen (sd_f->unrecognized_spec) > 0);
+ nm_assert (!sd_f->connection || NM_IS_CONNECTION (sd_f->connection));
+ nm_assert (!sd_f->storage || NM_IS_SETTINGS_STORAGE (sd_f->storage));
+ nm_assert ((!!sd_f->connection) == (!!sd_f->storage));
+
+ nm_assert (NM_STR_HAS_PREFIX (sd_f->filename, IFCFG_DIR));
- uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new));
- connection_by_uuid = g_hash_table_lookup (priv->connections, uuid);
+ nm_assert (sd_f == _storages_by_filename (&priv->storages, sd_f->filename));
- if ( connection
- && connection != connection_by_uuid) {
+ nm_assert ((!!sd_f->connection) + (!!sd_f->unmanaged_spec) + (!!sd_f->unrecognized_spec) == 1);
- if ( (protect_existing_connection && connection_by_uuid != NULL)
- || (protected_connections && g_hash_table_contains (protected_connections, connection))) {
- NMIfcfgConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection;
+ if (sd_f->connection) {
+ const char *uuid = nm_settings_storage_get_uuid (sd_f->storage);
+ const char *k;
- if (source)
- _LOGW ("cannot update protected connection "NM_IFCFG_CONNECTION_LOG_FMT" due to conflicting UUID %s", NM_IFCFG_CONNECTION_LOG_ARG (conflicting), uuid);
+ nm_assert (nm_streq0 (nm_connection_get_uuid (sd_f->connection), uuid));
+
+ if (!g_hash_table_lookup_extended (priv->storages.uuid_idx, uuid, (gpointer *) &k, (gpointer *) &sd_u))
+ nm_assert_not_reached ();
+
+ nm_assert (uuid == k);
+ nm_assert (sd_f == sd_u);
+
+ nm_assert (_nm_connection_verify (sd_f->connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
+
+ nm_assert (nm_streq (sd_f->filename, nm_settings_storage_get_filename (sd_f->storage)));
+
+ n_uuid++;
+ } else if (sd_f->unmanaged_spec)
+ g_hash_table_add (h_unmanaged, sd_f->unmanaged_spec);
else
- _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (conflicting));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Cannot update protected connection due to conflicting UUID");
- return NULL;
+ g_hash_table_add (h_unrecognized, sd_f->unrecognized_spec);
}
- /* The new connection has a different UUID then the original one that we
- * are about to update. Remove @connection. */
- remove_connection (self, connection);
+ nm_assert (g_hash_table_size (priv->storages.uuid_idx) == n_uuid);
+
+ nm_assert (nm_utils_hashtable_same_keys (h_unmanaged, priv->unmanaged_specs));
+ nm_assert (nm_utils_hashtable_same_keys (h_unrecognized, priv->unrecognized_specs));
}
+#endif
+}
- /* Check if the found connection with the same UUID is not protected from updating. */
- if ( connection_by_uuid
- && ( (!connection && protect_existing_connection)
- || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) {
- if (source)
- _LOGW ("cannot update connection due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid));
- else
- _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Skip updating protected connection during reload");
- return NULL;
- }
-
- /* Evaluate unmanaged/unrecognized flags. */
- if (connection_by_uuid)
- old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_by_uuid);
- new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_new);
- unmanaged_changed = g_strcmp0 (old_unmanaged, new_unmanaged);
-
- if (connection_by_uuid)
- old_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_by_uuid);
- new_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_new);
- unrecognized_changed = g_strcmp0 (old_unrecognized, new_unrecognized);
-
- if (connection_by_uuid) {
- const char *old_path;
-
- old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid));
-
- if ( !unmanaged_changed
- && !unrecognized_changed
- && nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
- NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
- if ( old_path
- && !nm_streq0 (old_path, full_path)) {
- _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT" without other changes",
- nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)),
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- }
- } else {
+/*****************************************************************************/
- /*******************************************************
- * UPDATE
- *******************************************************/
+static gboolean
+load_connection_impl (NMSIfcfgRHPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ 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 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 (source)
- _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT" from %s", NM_IFCFG_CONNECTION_LOG_ARG (connection_new), NM_IFCFG_CONNECTION_LOG_PATH (old_path));
- else if (nm_streq0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new))))
- _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- else if (old_path)
- _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT, old_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("update and persist "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-
- g_object_set (connection_by_uuid,
- NM_IFCFG_CONNECTION_UNMANAGED_SPEC, new_unmanaged,
- NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, new_unrecognized,
- NULL);
-
- if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "ifcfg-update",
- &local)) {
- /* Shouldn't ever get here as 'connection_new' was verified by the reader already
- * and the UUID did not change. */
- g_assert_not_reached ();
- }
- g_assert_no_error (local);
-
- if (new_unmanaged || new_unrecognized) {
- if (!old_unmanaged && !old_unrecognized) {
- /* ref connection first, because we put it into priv->connections below.
- * Emitting signal-removed might otherwise delete it. */
- g_object_ref (connection_by_uuid);
-
- /* Unexport the connection by telling the settings service it's
- * been removed.
- */
- nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection_by_uuid));
-
- /* signal_remove() will end up removing the connection from our hash,
- * so add it back now.
- */
- g_hash_table_insert (priv->connections,
- g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_by_uuid))),
- connection_by_uuid /* we took reference above and pass it on */);
- }
+ 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);
+ }
+
+ 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,
+ &unhandled_spec,
+ &local,
+ &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 {
- if (old_unmanaged /* && !new_unmanaged */) {
- _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" and its device because NM_CONTROLLED was true.",
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_by_uuid));
- } else if (old_unrecognized /* && !new_unrecognized */) {
- _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" because it is now a recognized type.",
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_by_uuid));
- }
+ _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;
+ }
+
+ _LOGT ("load[\"%s\"]: failed to load connection: %s", ifcfg_path, local->message);
+ g_propagate_error (error, g_steal_pointer (&local));
+ return FALSE;
+ }
+
+ 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 (unmanaged_changed)
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
- if (unrecognized_changed)
- _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+ 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;
}
- nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path);
- g_object_unref (connection_new);
- return connection_by_uuid;
- } else {
- /*******************************************************
- * ADD
- *******************************************************/
+ 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 {
- if (source)
- _LOGI ("add connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("new connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- g_hash_table_insert (priv->connections,
- g_strdup (uuid),
- connection_new /* take reference */);
-
- g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED,
- G_CALLBACK (connection_removed_cb),
- self);
-
- if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) {
- _LOGI ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" due to NM_CONTROLLED=no. Unmanaged: %s.",
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new),
- nm_ifcfg_connection_get_unmanaged_spec (connection_new));
- } else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new))
- _LOGW ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" of unrecognized type.", NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-
- if (!source) {
- /* Only raise the signal if we were called without source, i.e. if we read the connection from file.
- * Otherwise, we were called by add_connection() which does not expect the signal. */
- if (nm_ifcfg_connection_get_unmanaged_spec (connection_new))
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
- else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new))
- _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+ 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 {
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_new));
+ 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);
}
- return connection_new;
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+ return TRUE;
}
-}
-static GHashTable *
-_paths_from_connections (GHashTable *connections)
-{
- GHashTableIter iter;
- NMIfcfgConnection *connection;
- GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal);
+ if (!connection) {
+
+ 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;
+ }
- g_hash_table_iter_init (&iter, connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection));
+ 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;
+ }
- if (path)
- g_hash_table_add (paths, (void *) path);
+ 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);
+
+ 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);
+
+ 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;
}
- return paths;
-}
-static int
-_sort_paths (const char **f1, const char **f2, GHashTable *paths)
-{
- struct stat st;
- gboolean c1, c2;
- gint64 m1, m2;
+ if (sd_f == sd_u) {
+
+ connection_changed = !_connection_equals (connection, sd_f->connection);
+
+ 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);
+ }
- c1 = !!g_hash_table_contains (paths, *f1);
- c2 = !!g_hash_table_contains (paths, *f2);
- if (c1 != c2)
- return c1 ? -1 : 1;
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+ return TRUE;
+ }
+
+ 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;
+}
- m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- if (m1 != m2)
- return m1 > m2 ? -1 : 1;
+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;
- return strcmp (*f1, *f2);
+ nm_assert_self (self);
+ success = load_connection_impl (self, filename, out_storage, out_connection, out_storage_replaced, error);
+ nm_assert_self (self);
+ return success;
}
static void
-read_connections (SettingsPluginIfcfg *plugin)
+reload_connections (NMSettingsPlugin *plugin,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+ 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;
- GError *err = NULL;
- const char *item;
- GHashTable *alive_connections;
- GHashTableIter iter;
- NMIfcfgConnection *connection;
- GPtrArray *dead_connections = NULL;
- guint i;
- GPtrArray *filenames;
- GHashTable *paths;
-
- dir = g_dir_open (IFCFG_DIR, 0, &err);
+ 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);
+
+ 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);
+
+ /* load the list of filenames... */
+
+ dir = g_dir_open (IFCFG_DIR, 0, &local);
if (!dir) {
- _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, err->message);
- g_error_free (err);
- return;
+ _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;
+
+ rd = g_hash_table_lookup (rd_idx_by_filename, &real_path);
+ if (rd)
+ continue;
+
+ if (stat (real_path, &st) != 0) {
+ int errsv = errno;
+
+ _LOGT ("load[%s]: failure to stat file: %s", real_path, nm_strerror_native (errsv));
+ continue;
+ }
+
+ 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);
}
- alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+ /* load the connections/unhandled-spec from the filenames... */
+
+ c_list_sort (&rd_lst_head, _read_data_sort_by_mtime, NULL);
+
+ 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;
+
+ 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 (unhandled_spec) {
+ g_clear_object (&rd->connection);
+ if (!_parse_unhandled_spec (unhandled_spec,
+ &rd->unmanaged_spec,
+ &rd->unrecognized_spec))
+ goto destroy_rd_1;
+ continue;
+ }
+
+ nm_assert (NM_IS_CONNECTION (rd->connection));
+ nm_assert (_nm_connection_verify (rd->connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
- filenames = g_ptr_array_new_with_free_func (g_free);
- while ((item = g_dir_read_name (dir))) {
- char *full_path, *real_path;
+ nmtst_connection_assert_unchanging (rd->connection);
- full_path = g_build_filename (IFCFG_DIR, item, NULL);
- real_path = utils_detect_ifcfg_path (full_path, TRUE);
+ uuid = nm_connection_get_uuid (rd->connection);
+ nm_assert (nm_utils_is_uuid (uuid));
- if (real_path)
- g_ptr_array_add (filenames, real_path);
- g_free (full_path);
+ 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;
+ }
+
+ 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);
}
- g_dir_close (dir);
- /* While reloading, we don't replace connections that we already loaded while
- * iterating over the files.
+ /* 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.
*
- * To have sensible, reproducible behavior, sort the paths by last modification
- * time preferring older files.
- */
- paths = _paths_from_connections (priv->connections);
- g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths);
- g_hash_table_destroy (paths);
-
- for (i = 0; i < filenames->len; i++) {
- connection = update_connection (plugin, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL);
- if (connection)
- g_hash_table_add (alive_connections, connection);
- }
- g_ptr_array_free (filenames, TRUE);
-
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- if ( !g_hash_table_contains (alive_connections, connection)
- && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) {
- if (!dead_connections)
- dead_connections = g_ptr_array_new ();
- g_ptr_array_add (dead_connections, connection);
+ * 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);
+
+
+ _storages_init (&new_storages);
+
+
+ /* first find and remove the storage-data that is gone entirely. */
+
+ c_list_for_each_entry_safe (sd, sd_safe, &priv->storages.sd_lst_head, sd_lst) {
+
+ rd = g_hash_table_lookup (rd_idx_by_filename, &sd->filename);
+
+ 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);
+ }
+ 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);
}
}
- g_hash_table_destroy (alive_connections);
- if (dead_connections) {
- for (i = 0; i < dead_connections->len; i++)
- remove_connection (plugin, dead_connections->pdata[i]);
- g_ptr_array_free (dead_connections, TRUE);
+ c_list_for_each_entry (rd, &rd_lst_head, rd_lst) {
+
+ sd = _storage_data_new (&new_storages, rd->filename);
+
+ sd->unmanaged_spec = g_strdup (rd->unmanaged_spec);
+ sd->unrecognized_spec = g_strdup (rd->unrecognized_spec);
+ sd->connection = g_object_ref (rd->connection);
+
+ if (!rd->connection)
+ 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 ();
}
+
+ 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;
+
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ 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;
+ }
+
+ _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);
+ }
+ }
+
+
+ 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];
+
+ if (!g_hash_table_add (h_unique, storage))
+ continue;
+
+ 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);
+ }
+ }
+
+
+ _unhandled_specs_reset (self);
+
+ nm_assert_self (self);
}
-static GSList *
-get_connections (NMSettingsPlugin *config)
+typedef struct {
+ GHashTable *filename_idx;
+ const char *allowed_filename;
+} AllowFilenameData;
+
+#define ALLOW_FILENAME_DATA(_filename_idx, _allowed_filename) \
+ (&((AllowFilenameData) { \
+ .filename_idx = (_filename_idx), \
+ .allowed_filename = (_allowed_filename), \
+ }))
+
+static gboolean
+_allow_filename_cb (const char *filename,
+ gpointer user_data)
{
- SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config);
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin);
- GSList *list = NULL;
- GHashTableIter iter;
- NMIfcfgConnection *connection;
+ 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;
+}
- if (!priv->initialized) {
- read_connections (plugin);
- priv->initialized = TRUE;
+static gboolean
+add_connection (NMSettingsPlugin *plugin,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_unref_object NMConnection *reread = NULL;
+ gs_free char *full_filename = NULL;
+ GError *local = NULL;
+ const char *uuid;
+ StorageData *sd_f;
+ gboolean reread_same;
+
+ nm_assert_self (self);
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (out_storage && !*out_storage);
+ nm_assert (out_connection && !*out_connection);
+
+ 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;
}
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
- if ( !nm_ifcfg_connection_get_unmanaged_spec (connection)
- && !nm_ifcfg_connection_get_unrecognized_spec (connection))
- list = g_slist_prepend (list, connection);
+ if (!nms_ifcfg_rh_writer_write_connection (connection,
+ IFCFG_DIR,
+ NULL,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, NULL),
+ &full_filename,
+ &reread,
+ &reread_same,
+ &local)) {
+ _LOGT ("add: %s (%s): failed to add connection: %s",
+ nm_connection_get_uuid (connection),
+ nm_connection_get_id (connection),
+ local->message);
+ g_propagate_error (error, local);
+ return FALSE;
}
- return list;
+ if ( !reread
+ || reread_same)
+ nm_g_object_ref_set (&reread, connection);
+
+ uuid = nm_connection_get_uuid (reread);
+
+ nm_assert (full_filename && full_filename[0] == '/');
+
+ _LOGT ("add[%s]: profile %s (%s) written%s",
+ full_filename,
+ uuid,
+ nm_connection_get_id (reread),
+ _connection_equals (connection, reread) ? "" : " (profile mutated)");
+
+ sd_f = _storage_data_new (&priv->storages, full_filename);
+
+ sd_f->connection = g_steal_pointer (&reread);
+ sd_f->storage = nm_settings_storage_new (plugin, uuid, full_filename);
+
+ nmtst_connection_assert_unchanging (sd_f->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 ();
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+
+ nm_assert_self (self);
+
+ return TRUE;
}
static gboolean
-load_connection (NMSettingsPlugin *config,
- const char *filename)
+update_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
{
- SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config);
- NMIfcfgConnection *connection;
- char *ifcfg_path;
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ StorageData *sd_f;
+ const char *filename;
+ const char *uuid;
+ GError *local = NULL;
+ gs_unref_object NMConnection *reread = NULL;
+ gboolean connection_changed;
+ gboolean reread_same;
- if (!nm_utils_file_is_in_path (filename, IFCFG_DIR))
- return FALSE;
+ nm_assert_self (self);
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (nm_connection_verify (connection, NULL));
+ nm_assert (!error || !*error);
+
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ nm_assert (nm_streq0 (uuid, nm_connection_get_uuid (connection)));
+
+ filename = nm_settings_storage_get_filename (storage);
+
+ nm_assert (filename);
- /* 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);
- if (!ifcfg_path)
+ sd_f = _storages_by_filename (&priv->storages, filename);
+
+ nm_assert (sd_f);
+ nm_assert (sd_f == _storages_by_uuid (&priv->storages, uuid));
+
+ if (!nms_ifcfg_rh_writer_write_connection (connection,
+ IFCFG_DIR,
+ sd_f->filename,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, sd_f->filename),
+ NULL,
+ &reread,
+ &reread_same,
+ &local)) {
+ _LOGT ("add: %s (%s): failed to update connection: %s",
+ nm_connection_get_uuid (connection),
+ nm_connection_get_id (connection),
+ local->message);
+ g_propagate_error (error, local);
return FALSE;
+ }
+
+ if ( !reread
+ || reread_same)
+ nm_g_object_ref_set (&reread, connection);
+
+ connection_changed = !_connection_equals (reread, sd_f->connection);
+
+ _LOGT ("update[%s]: %s (%s) %s%s",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (connection),
+ connection_changed ? "updated" : "unchanged",
+ _connection_equals (reread, connection) ? "" : " (profile mutated)");
+
+ if (connection_changed)
+ nm_g_object_ref_set (&sd_f->connection, reread);
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+
+ nm_assert_self (self);
+
+ return TRUE;
+}
+
+static gboolean
+delete_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error)
+{
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ StorageData *sd_f;
+ const char *operation_message;
+ const char *filename;
+ const char *uuid;
+
+ nm_assert_self (self);
+ nm_assert (!error || !*error);
+
+ filename = nm_settings_storage_get_filename (storage);
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ nm_assert (filename);
+
+ sd_f = _storages_by_filename (&priv->storages, filename);
+
+ g_return_val_if_fail (sd_f, TRUE);
- connection = find_by_path (plugin, ifcfg_path);
- update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL);
- if (!connection)
- connection = find_by_path (plugin, ifcfg_path);
+ if (!remove_from_disk)
+ operation_message = "only dropped from memory";
+ else {
+ gs_free char *keyfile = utils_get_keys_path (filename);
+ gs_free char *routefile = utils_get_route_path (filename);
+ gs_free char *route6file = utils_get_route6_path (filename);
+ const char *const files[] = { filename, keyfile, routefile, route6file };
+ gboolean any_deleted = FALSE;
+ gboolean any_failure = FALSE;
+ int i;
- g_free (ifcfg_path);
- return (connection != NULL);
+ for (i = 0; i < G_N_ELEMENTS (files); i++) {
+ int errsv;
+
+ if (unlink (files[i]) == 0) {
+ any_deleted = TRUE;
+ continue;
+ }
+ errsv = errno;
+ if (errsv == ENOENT)
+ continue;
+
+ _LOGW ("delete[%s]: failure to delete file \"%s\": %s",
+ filename,
+ files[i],
+ nm_strerror_native (errsv));
+ any_failure = TRUE;
+ }
+ if (any_failure)
+ operation_message = "failed to delete files from disk";
+ else if (any_deleted)
+ operation_message = "deleted from disk";
+ else
+ operation_message = "does not exist on disk";
+ }
+
+ _LOGT ("delete[%s]: %s (%s) %s",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (sd_f->connection),
+ operation_message);
+
+ if (!g_hash_table_remove (priv->storages.uuid_idx, uuid))
+ nm_assert_not_reached ();
+
+ if (!g_hash_table_remove (priv->storages.filename_idx, sd_f))
+ nm_assert_not_reached ();
+
+ nm_assert_self (self);
+
+ return TRUE;
}
+/*****************************************************************************/
+
static void
-reload_connections (NMSettingsPlugin *config)
+_unhandled_specs_reset (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config);
+ 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;
+
+ 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));
+ }
+
+ if (!nm_utils_hashtable_same_keys (unmanaged_specs, priv->unmanaged_specs)) {
+ g_hash_table_unref (priv->unmanaged_specs);
+ priv->unmanaged_specs = g_steal_pointer (&unmanaged_specs);
+ }
+ if (!nm_utils_hashtable_same_keys (unrecognized_specs, priv->unrecognized_specs)) {
+ g_hash_table_unref (priv->unrecognized_specs);
+ priv->unrecognized_specs = g_steal_pointer (&unrecognized_specs);
+ }
- read_connections (plugin);
+ if (!unmanaged_specs)
+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
+ if (!unrecognized_specs)
+ _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
}
-static GSList *
-get_unhandled_specs (NMSettingsPlugin *config,
- const char *property)
-{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) config);
- GSList *list = NULL, *list_iter;
- GHashTableIter iter;
- gpointer connection;
- char *spec;
- gboolean found;
-
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, &connection)) {
- g_object_get (connection, property, &spec, NULL);
- if (spec) {
- /* Ignore duplicates */
- for (list_iter = list, found = FALSE; list_iter; list_iter = g_slist_next (list_iter)) {
- if (g_str_equal (list_iter->data, spec)) {
- found = TRUE;
- break;
- }
- }
- if (found)
- g_free (spec);
- else
- list = g_slist_prepend (list, spec);
+static void
+_unhandled_specs_merge_read_data (NMSIfcfgRHPlugin *self,
+ CList *rd_lst_head)
+{
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gboolean unmanaged_changed = FALSE;
+ gboolean unrecognized_changed = FALSE;
+ ReadData *rd;
+
+ 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)) {
+ unmanaged_changed = TRUE;
+ g_hash_table_add (priv->unmanaged_specs, g_strdup (rd->unmanaged_spec));
+ }
+ if ( rd->unrecognized_spec
+ && !g_hash_table_contains (priv->unrecognized_specs, rd->unrecognized_spec)) {
+ unrecognized_changed = TRUE;
+ g_hash_table_add (priv->unrecognized_specs, g_strdup (rd->unrecognized_spec));
}
}
- return list;
+
+ if (unmanaged_changed)
+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
+ if (unrecognized_changed)
+ _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
}
static GSList *
-get_unmanaged_specs (NMSettingsPlugin *config)
+_unhandled_specs_from_hashtable (GHashTable *hash)
{
- return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNMANAGED_SPEC);
+ gs_free const char **keys = NULL;
+ GSList *list = NULL;
+ guint i, l;
+
+ keys = nm_utils_strdict_get_keys (hash, TRUE, &l);
+ for (i = l; i > 0; ) {
+ i--;
+ list = g_slist_prepend (list, g_strdup (keys[i]));
+ }
+ return list;
}
static GSList *
-get_unrecognized_specs (NMSettingsPlugin *config)
+get_unmanaged_specs (NMSettingsPlugin *plugin)
{
- return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC);
+ return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unmanaged_specs);
}
-static NMSettingsConnection *
-add_connection (NMSettingsPlugin *config,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error)
+static GSList *
+get_unrecognized_specs (NMSettingsPlugin *plugin)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (config);
- gs_free char *path = NULL;
- gs_unref_object NMConnection *reread = NULL;
-
- if (save_to_disk) {
- if (!nms_ifcfg_rh_writer_write_connection (connection, IFCFG_DIR, NULL, NULL, NULL, &path, &reread, NULL, error))
- return NULL;
- } else {
- if (!nms_ifcfg_rh_writer_can_write_connection (connection, error))
- return NULL;
- }
- return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
+ return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unrecognized_specs);
}
+/*****************************************************************************/
+
static void
-impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
+impl_ifcfgrh_get_ifcfg_details (NMSIfcfgRHPlugin *self,
GDBusMethodInvocation *context,
const char *in_ifcfg)
{
- NMIfcfgConnection *connection;
- NMSettingConnection *s_con;
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_free char *ifcfg_path = NULL;
+ StorageData *sd_f;
const char *uuid;
const char *path;
- gs_free char *ifcfg_path = NULL;
- if (!g_path_is_absolute (in_ifcfg)) {
+ if (in_ifcfg[0] != '/') {
g_dbus_method_invocation_return_error (context,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -631,10 +1373,9 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
return;
}
- connection = find_by_path (plugin, ifcfg_path);
- if ( !connection
- || nm_ifcfg_connection_get_unmanaged_spec (connection)
- || nm_ifcfg_connection_get_unrecognized_spec (connection)) {
+ sd_f = _storages_by_filename (&priv->storages, ifcfg_path);
+ if ( !sd_f
+ || !sd_f->connection) {
g_dbus_method_invocation_return_error (context,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -642,25 +1383,16 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
return;
}
- s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)));
- if (!s_con) {
- g_dbus_method_invocation_return_error (context,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "unable to retrieve the connection setting");
- return;
- }
+ uuid = nm_settings_storage_get_uuid (sd_f->storage);
- uuid = nm_setting_connection_get_uuid (s_con);
- if (!uuid) {
- g_dbus_method_invocation_return_error (context,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "unable to get the UUID");
- return;
- }
+ /* It is ugly that the ifcfg-rh plugin needs to call back into NMSettings this
+ * way.
+ * There are alternatives (like invoking a signal), but they are all significant
+ * extra code (and performance overhead). So the quick and dirty solution here
+ * is likely to be simpler than getting this right (also from point of readability!).
+ */
+ path = nm_settings_get_dbus_path_for_uuid (nm_settings_get (), uuid);
- path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection));
if (!path) {
g_dbus_method_invocation_return_error (context,
NM_SETTINGS_ERROR,
@@ -676,9 +1408,9 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
/*****************************************************************************/
static void
-_dbus_clear (SettingsPluginIfcfg *self)
+_dbus_clear (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
guint id;
nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id);
@@ -700,7 +1432,7 @@ _dbus_connection_closed (GDBusConnection *connection,
gpointer user_data)
{
_LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME);
- _dbus_clear (SETTINGS_PLUGIN_IFCFG (user_data));
+ _dbus_clear (NMS_IFCFG_RH_PLUGIN (user_data));
/* Retry or recover? */
}
@@ -715,21 +1447,23 @@ _method_call (GDBusConnection *connection,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data);
- const char *ifcfg;
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (user_data);
- if ( !nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)
- || !nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) {
- g_dbus_method_invocation_return_error (invocation,
- G_DBUS_ERROR,
- G_DBUS_ERROR_UNKNOWN_METHOD,
- "Unknown method %s",
- method_name);
- return;
+ if (nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)) {
+ if (nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) {
+ const char *ifcfg;
+
+ g_variant_get (parameters, "(&s)", &ifcfg);
+ impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg);
+ return;
+ }
}
- g_variant_get (parameters, "(&s)", &ifcfg);
- impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_UNKNOWN_METHOD,
+ "Unknown method %s",
+ method_name);
}
static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO (
@@ -754,8 +1488,8 @@ _dbus_request_name_done (GObject *source_object,
gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
- SettingsPluginIfcfg *self;
- SettingsPluginIfcfgPrivate *priv;
+ NMSIfcfgRHPlugin *self;
+ NMSIfcfgRHPluginPrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *ret = NULL;
guint32 result;
@@ -764,8 +1498,8 @@ _dbus_request_name_done (GObject *source_object,
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
- self = SETTINGS_PLUGIN_IFCFG (user_data);
- priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ self = NMS_IFCFG_RH_PLUGIN (user_data);
+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
g_clear_object (&priv->dbus.cancellable);
@@ -812,8 +1546,8 @@ _dbus_create_done (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
- SettingsPluginIfcfg *self;
- SettingsPluginIfcfgPrivate *priv;
+ NMSIfcfgRHPlugin *self;
+ NMSIfcfgRHPluginPrivate *priv;
gs_free_error GError *error = NULL;
GDBusConnection *connection;
@@ -821,8 +1555,8 @@ _dbus_create_done (GObject *source_object,
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
- self = SETTINGS_PLUGIN_IFCFG (user_data);
- priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ self = NMS_IFCFG_RH_PLUGIN (user_data);
+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
g_clear_object (&priv->dbus.cancellable);
@@ -856,9 +1590,9 @@ _dbus_create_done (GObject *source_object,
}
static void
-_dbus_setup (SettingsPluginIfcfg *self)
+_dbus_setup (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
gs_free char *address = NULL;
gs_free_error GError *error = NULL;
@@ -886,9 +1620,9 @@ config_changed_cb (NMConfig *config,
NMConfigData *config_data,
NMConfigChangeFlags changes,
NMConfigData *old_data,
- SettingsPluginIfcfg *self)
+ NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv;
+ NMSIfcfgRHPluginPrivate *priv;
/* If the dbus connection for some reason is borked the D-Bus service
* won't be offered.
@@ -900,7 +1634,7 @@ config_changed_cb (NMConfig *config,
| NM_CONFIG_CHANGE_CAUSE_SIGUSR1))
return;
- priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
if ( !priv->dbus.connection
&& !priv->dbus.cancellable)
_dbus_setup (self);
@@ -909,23 +1643,26 @@ config_changed_cb (NMConfig *config,
/*****************************************************************************/
static void
-settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin)
+nms_ifcfg_rh_plugin_init (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+
+ priv->config = g_object_ref (nm_config_get ());
- priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+ 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);
}
static void
constructed (GObject *object)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object);
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
- G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->constructed (object);
+ G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->constructed (object);
- priv->config = nm_config_get ();
- g_object_add_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config);
g_signal_connect (priv->config,
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
G_CALLBACK (config_changed_cb),
@@ -937,40 +1674,44 @@ constructed (GObject *object)
static void
dispose (GObject *object)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object);
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
- if (priv->config) {
- g_object_remove_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config);
+ if (priv->config)
g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
- priv->config = NULL;
- }
+ /* FIXME(shutdown) we need a stop method so that we can unregistering the D-Bus service
+ * when NMSettings is shutting down, and not when the instance gets destroyed. */
_dbus_clear (self);
- if (priv->connections) {
- g_hash_table_destroy (priv->connections);
- priv->connections = NULL;
- }
+ nm_clear_pointer (&priv->storages.uuid_idx, g_hash_table_destroy);
+ nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy);
+
+ g_clear_object (&priv->config);
+
+ G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->dispose (object);
- G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->dispose (object);
+ nm_clear_pointer (&priv->unmanaged_specs, g_hash_table_destroy);
+ nm_clear_pointer (&priv->unrecognized_specs, g_hash_table_destroy);
}
static void
-settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass)
+nms_ifcfg_rh_plugin_class_init (NMSIfcfgRHPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
object_class->constructed = constructed;
- object_class->dispose = dispose;
+ object_class->dispose = dispose;
- plugin_class->get_connections = get_connections;
- plugin_class->add_connection = add_connection;
- plugin_class->load_connection = load_connection;
- plugin_class->reload_connections = reload_connections;
- plugin_class->get_unmanaged_specs = get_unmanaged_specs;
+ 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->add_connection = add_connection;
+ plugin_class->update_connection = update_connection;
+ plugin_class->delete_connection = delete_connection;
}
/*****************************************************************************/
@@ -978,5 +1719,5 @@ settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass)
G_MODULE_EXPORT NMSettingsPlugin *
nm_settings_plugin_factory (void)
{
- return g_object_new (SETTINGS_TYPE_PLUGIN_IFCFG, NULL);
+ return g_object_new (NMS_TYPE_IFCFG_RH_PLUGIN, NULL);
}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
index 88c3dc7939..1db36083ff 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
@@ -20,19 +20,19 @@
* Copyright (C) 2007 - 2008 Red Hat, Inc.
*/
-#ifndef _PLUGIN_H_
-#define _PLUGIN_H_
+#ifndef __NMS_IFCFG_RH_PLUGIN_H__
+#define __NMS_IFCFG_RH_PLUGIN_H__
-#define SETTINGS_TYPE_PLUGIN_IFCFG (settings_plugin_ifcfg_get_type ())
-#define SETTINGS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfg))
-#define SETTINGS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass))
-#define SETTINGS_IS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFCFG))
-#define SETTINGS_IS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFCFG))
-#define SETTINGS_PLUGIN_IFCFG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass))
+#define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type ())
+#define NMS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin))
+#define NMS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass))
+#define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_PLUGIN))
+#define NMS_IS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_PLUGIN))
+#define NMS_IFCFG_RH_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass))
-typedef struct _SettingsPluginIfcfg SettingsPluginIfcfg;
-typedef struct _SettingsPluginIfcfgClass SettingsPluginIfcfgClass;
+typedef struct _NMSIfcfgRHPlugin NMSIfcfgRHPlugin;
+typedef struct _NMSIfcfgRHPluginClass NMSIfcfgRHPluginClass;
-GType settings_plugin_ifcfg_get_type (void);
+GType nms_ifcfg_rh_plugin_get_type (void);
-#endif /* _PLUGIN_H_ */
+#endif /* __NMS_IFCFG_RH_PLUGIN_H__ */
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 99e48d6b2a..177c60ff4a 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -6028,20 +6028,3 @@ nmtst_connection_from_file (const char *filename,
error,
NULL);
}
-
-guint
-devtimeout_from_file (const char *filename)
-{
- shvarFile *ifcfg;
- guint devtimeout;
-
- g_return_val_if_fail (filename != NULL, 0);
-
- ifcfg = svOpenFile (filename, NULL);
- if (!ifcfg)
- return 0;
-
- devtimeout = svGetValueInt64 (ifcfg, "DEVTIMEOUT", 10, 0, G_MAXUINT, 0);
- svCloseFile (ifcfg);
- return devtimeout;
-}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
index be402565a8..8008e052bc 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
@@ -17,8 +17,8 @@
* Copyright (C) 2008 Red Hat, Inc.
*/
-#ifndef __READER_H__
-#define __READER_H__
+#ifndef __NMS_IFCFG_RH_READER_H__
+#define __NMS_IFCFG_RH_READER_H__
#include "nm-connection.h"
@@ -27,12 +27,10 @@ NMConnection *connection_from_file (const char *filename,
GError **error,
gboolean *out_ignore_error);
-guint devtimeout_from_file (const char *filename);
-
NMConnection *nmtst_connection_from_file (const char *filename,
const char *network_file,
const char *test_type,
char **out_unhandled,
GError **error);
-#endif /* __READER_H__ */
+#endif /* __NMS_IFCFG_RH_READER_H__ */
diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build
index 42edd4388a..365ae1a9c5 100644
--- a/src/settings/plugins/ifupdown/meson.build
+++ b/src/settings/plugins/ifupdown/meson.build
@@ -15,7 +15,6 @@ libnms_ifupdown_core = static_library(
)
sources = files(
- 'nms-ifupdown-connection.c',
'nms-ifupdown-plugin.c',
)
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
deleted file mode 100644
index 13187c4454..0000000000
--- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* NetworkManager system settings service (ifupdown)
- *
- * Alexander Sack <asac@ubuntu.com>
- *
- * 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.
- *
- * (C) Copyright 2007,2008 Canonical Ltd.
- */
-
-#include "nm-default.h"
-
-#include "nms-ifupdown-connection.h"
-
-#include <glib/gstdio.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-utils.h"
-#include "nm-setting-wireless-security.h"
-#include "settings/nm-settings-connection.h"
-#include "settings/nm-settings-plugin.h"
-
-#include "nms-ifupdown-parser.h"
-
-/*****************************************************************************/
-
-struct _NMIfupdownConnection {
- NMSettingsConnection parent;
-};
-
-struct _NMIfupdownConnectionClass {
- NMSettingsConnectionClass parent;
-};
-
-G_DEFINE_TYPE (NMIfupdownConnection, nm_ifupdown_connection, NM_TYPE_SETTINGS_CONNECTION)
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME "ifupdown"
-#define _NMLOG_DOMAIN LOGD_SETTINGS
-#define _NMLOG(level, ...) \
- nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
- "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
- _NMLOG_PREFIX_NAME": " \
- _NM_UTILS_MACRO_REST (__VA_ARGS__))
-
-/*****************************************************************************/
-
-static void
-nm_ifupdown_connection_init (NMIfupdownConnection *connection)
-{
-}
-
-NMIfupdownConnection *
-nm_ifupdown_connection_new (if_block *block)
-{
- NMIfupdownConnection *connection;
- GError *error = NULL;
-
- g_return_val_if_fail (block != NULL, NULL);
-
- connection = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL);
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (!ifupdown_update_connection_from_if_block (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)),
- block,
- &error)) {
- _LOGW ("invalid connection read from /etc/network/interfaces: %s",
- error->message);
- g_object_unref (connection);
- return NULL;
- }
-
- return connection;
-}
-
-static void
-nm_ifupdown_connection_class_init (NMIfupdownConnectionClass *ifupdown_connection_class)
-{
-}
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h b/src/settings/plugins/ifupdown/nms-ifupdown-connection.h
deleted file mode 100644
index eee3ae0ff1..0000000000
--- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* NetworkManager system settings service (ifupdown)
- *
- * Alexander Sack <asac@ubuntu.com>
- *
- * 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.
- *
- * (C) Copyright 2008 Canonical Ltd.
- */
-
-#ifndef __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__
-#define __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__
-
-#include "settings/nm-settings-connection.h"
-
-#include "nms-ifupdown-interface-parser.h"
-
-#define NM_TYPE_IFUPDOWN_CONNECTION (nm_ifupdown_connection_get_type ())
-#define NM_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnection))
-#define NM_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass))
-#define NM_IS_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFUPDOWN_CONNECTION))
-#define NM_IS_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFUPDOWN_CONNECTION))
-#define NM_IFUPDOWN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass))
-
-typedef struct _NMIfupdownConnection NMIfupdownConnection;
-typedef struct _NMIfupdownConnectionClass NMIfupdownConnectionClass;
-
-GType nm_ifupdown_connection_get_type (void);
-
-NMIfupdownConnection *nm_ifupdown_connection_new (if_block *block);
-
-#endif /* __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index f83c5ea0ef..7e2cb5b92f 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -651,22 +651,20 @@ update_ip6_setting_from_if_block (NMConnection *connection,
return TRUE;
}
-gboolean
-ifupdown_update_connection_from_if_block (NMConnection *connection,
- if_block *block,
- GError **error)
+NMConnection *
+ifupdown_new_connection_from_if_block (if_block *block,
+ GError **error)
{
+ gs_unref_object NMConnection *connection = NULL;
const char *type;
gs_free char *idstr = NULL;
gs_free char *uuid = NULL;
NMSettingConnection *s_con;
- gboolean success = FALSE;
- s_con = nm_connection_get_setting_connection (connection);
- if (!s_con) {
- s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
- nm_connection_add_setting (connection, NM_SETTING (s_con));
- }
+ connection = nm_simple_connection_new ();
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
type = _ifupdownplugin_guess_connection_type (block);
idstr = g_strconcat ("Ifupdown (", block->name, ")", NULL);
@@ -681,7 +679,7 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
NULL);
- _LOGI ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
+ _LOGD ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
block->name, type, idstr, nm_setting_connection_get_uuid (s_con));
if (nm_streq (type, NM_SETTING_WIRED_SETTING_NAME))
@@ -691,13 +689,16 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
update_wireless_security_setting_from_if_block (connection, block);
}
- if (ifparser_haskey (block, "inet6"))
- success = update_ip6_setting_from_if_block (connection, block, error);
- else
- success = update_ip4_setting_from_if_block (connection, block, error);
+ if (ifparser_haskey (block, "inet6")) {
+ if (!update_ip6_setting_from_if_block (connection, block, error))
+ return FALSE;
+ } else {
+ if (!update_ip4_setting_from_if_block (connection, block, error))
+ return FALSE;
+ }
- if (success == TRUE)
- success = nm_connection_verify (connection, error);
+ if (!nm_connection_normalize (connection, NULL, NULL, error))
+ return NULL;
- return success;
+ return g_steal_pointer (&connection);
}
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
index c17e92392f..2e5b5e51a7 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
@@ -19,16 +19,13 @@
* (C) Copyright 2008 Canonical Ltd.
*/
-#ifndef __PARSER_H__
-#define __PARSER_H__
+#ifndef __NMS_IFUPDOWN_PARSER_H__
+#define __NMS_IFUPDOWN_PARSER_H__
#include "nm-connection.h"
-
#include "nms-ifupdown-interface-parser.h"
-gboolean
-ifupdown_update_connection_from_if_block (NMConnection *connection,
- if_block *block,
- GError **error);
+NMConnection *ifupdown_new_connection_from_if_block (if_block *block,
+ GError **error);
-#endif /* __PARSER_H__ */
+#endif /* __NMS_IFUPDOWN_PARSER_H__ */
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 23f4015609..f8941021e5 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -24,23 +24,12 @@
#include "nms-ifupdown-plugin.h"
-#include <arpa/inet.h>
-#include <gmodule.h>
-
-#include "nm-setting-connection.h"
-#include "nm-dbus-interface.h"
-#include "settings/nm-settings-plugin.h"
-#include "nm-setting-ip4-config.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-ppp.h"
-#include "nm-utils.h"
#include "nm-core-internal.h"
-#include "NetworkManagerUtils.h"
#include "nm-config.h"
+#include "settings/nm-settings-plugin.h"
+#include "settings/nm-settings-storage.h"
#include "nms-ifupdown-interface-parser.h"
-#include "nms-ifupdown-connection.h"
#include "nms-ifupdown-parser.h"
#define ENI_INTERFACES_FILE "/etc/network/interfaces"
@@ -50,28 +39,33 @@
/*****************************************************************************/
typedef struct {
+ NMConnection *connection;
+ NMSettingsStorage *storage;
+} StorageData;
+
+typedef struct {
/* Stores an entry for blocks/interfaces read from /e/n/i and (if exists)
- * the NMIfupdownConnection associated with the block.
+ * the StorageData associated with the block.
*/
GHashTable *eni_ifaces;
bool ifupdown_managed:1;
bool initialized:1;
-} SettingsPluginIfupdownPrivate;
+} NMSIfupdownPluginPrivate;
-struct _SettingsPluginIfupdown {
+struct _NMSIfupdownPlugin {
NMSettingsPlugin parent;
- SettingsPluginIfupdownPrivate _priv;
+ NMSIfupdownPluginPrivate _priv;
};
-struct _SettingsPluginIfupdownClass {
+struct _NMSIfupdownPluginClass {
NMSettingsPluginClass parent;
};
-G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTINGS_PLUGIN)
+G_DEFINE_TYPE (NMSIfupdownPlugin, nms_ifupdown_plugin, NM_TYPE_SETTINGS_PLUGIN)
-#define SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfupdown, SETTINGS_IS_PLUGIN_IFUPDOWN)
+#define NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfupdownPlugin, NMS_IS_IFUPDOWN_PLUGIN)
/*****************************************************************************/
@@ -85,50 +79,60 @@ G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTING
/*****************************************************************************/
-static void initialize (SettingsPluginIfupdown *self);
+static void initialize (NMSIfupdownPlugin *self);
/*****************************************************************************/
-/* Returns the plugins currently known list of connections. The returned
- * list is freed by the system settings service.
- */
-static GSList*
-get_connections (NMSettingsPlugin *plugin)
+static void
+_storage_data_destroy (StorageData *sd)
{
- SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
- GSList *list = NULL;
+ g_object_unref (sd->connection);
+ nm_g_object_unref (sd->storage);
+ g_slice_free (StorageData, sd);
+}
+
+/*****************************************************************************/
+
+static void
+reload_connections (NMSettingsPlugin *plugin,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
+{
+ NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
GHashTableIter iter;
- void *value;
+ StorageData *sd;
if (G_UNLIKELY (!priv->initialized))
initialize (self);
if (!priv->ifupdown_managed) {
- _LOGD ("get_connections: not connections due to managed=false");
- return NULL;
+ _LOGD ("load: no connections due to managed=false");
+ return;
}
g_hash_table_iter_init (&iter, priv->eni_ifaces);
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- if (value)
- list = g_slist_prepend (list, value);
- }
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) {
+ if (!sd)
+ continue;
- _LOGD ("get_connections: %u connections", g_slist_length (list));
- return list;
+ _LOGD ("load: %s (%s)",
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection));
+ callback (plugin,
+ sd->storage,
+ sd->connection,
+ user_data);
+ }
}
-/*
- * Return a list of device specifications which NetworkManager should not
- * manage. Returned list will be freed by the system settings service, and
- * each element must be allocated using g_malloc() or its variants.
- */
+/*****************************************************************************/
+
static GSList*
get_unmanaged_specs (NMSettingsPlugin *plugin)
{
- SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+ NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
GSList *specs = NULL;
GHashTableIter iter;
const char *iface;
@@ -151,15 +155,15 @@ get_unmanaged_specs (NMSettingsPlugin *plugin)
/*****************************************************************************/
static void
-initialize (SettingsPluginIfupdown *self)
+initialize (NMSIfupdownPlugin *self)
{
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
gs_unref_hashtable GHashTable *auto_ifaces = NULL;
nm_auto_ifparser if_parser *parser = NULL;
if_block *block;
- GHashTableIter con_iter;
+ GHashTableIter h_iter;
const char *block_name;
- NMIfupdownConnection *conn;
+ StorageData *sd;
nm_assert (!priv->initialized);
priv->initialized = TRUE;
@@ -176,6 +180,12 @@ initialize (SettingsPluginIfupdown *self)
}
if (nm_streq (block->type, "iface")) {
+ gs_free_error GError *local = NULL;
+ gs_unref_object NMConnection *connection = NULL;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ const char *uuid = NULL;
+ StorageData *sd_repl;
+
/* Bridge configuration */
if (g_str_has_prefix (block->name, "br")) {
/* Try to find bridge ports */
@@ -208,13 +218,13 @@ initialize (SettingsPluginIfupdown *self)
if (nm_streq (token, "none"))
continue;
if (state == 0) {
- conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
- if (!conn) {
+ sd = g_hash_table_lookup (priv->eni_ifaces, block->name);
+ if (!sd) {
_LOGD ("parse: adding bridge port \"%s\"", token);
g_hash_table_insert (priv->eni_ifaces, g_strdup (token), NULL);
} else {
_LOGD ("parse: adding bridge port \"%s\" (have connection %s)", token,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+ nm_settings_storage_get_uuid (sd->storage));
}
}
}
@@ -226,54 +236,71 @@ initialize (SettingsPluginIfupdown *self)
if (nm_streq (block->name, "lo"))
continue;
- /* Remove any connection for this block that was previously found */
- conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
- if (conn) {
+ sd_repl = g_hash_table_lookup (priv->eni_ifaces, block->name);
+ if (sd_repl) {
+ storage = g_steal_pointer (&sd_repl->storage);
_LOGD ("parse: replace connection \"%s\" (%s)",
block->name,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
- nm_settings_connection_delete (NM_SETTINGS_CONNECTION (conn), NULL);
+ nm_settings_storage_get_uuid (sd_repl->storage));
g_hash_table_remove (priv->eni_ifaces, block->name);
}
- /* add the new connection */
- conn = nm_ifupdown_connection_new (block);
- if (conn) {
- _LOGD ("parse: adding connection \"%s\" (%s)", block->name,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
- } else
- _LOGD ("parse: adding place holder for connection \"%s\"", block->name);
- g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), conn);
+ connection = ifupdown_new_connection_from_if_block (block, &local);
+
+ if (!connection) {
+ _LOGD ("parse: adding place holder for \"%s\"%s%s%s",
+ block->name,
+ NM_PRINT_FMT_QUOTED (local, " (", local->message, ")", ""));
+ sd = NULL;
+ } else {
+ uuid = nm_connection_get_uuid (connection);
+ sd = g_slice_new (StorageData);
+ *sd = (StorageData) {
+ .connection = g_steal_pointer (&connection),
+ .storage = g_steal_pointer (&storage)
+ ?: nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, NULL),
+ };
+ _LOGD ("parse: adding connection \"%s\" (%s)", block->name, uuid);
+ }
+
+ g_hash_table_replace (priv->eni_ifaces, g_strdup (block->name), sd);
continue;
}
if (nm_streq (block->type, "mapping")) {
- conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
- if (!conn) {
+ sd = g_hash_table_lookup (priv->eni_ifaces, block->name);
+ if (!sd) {
_LOGD ("parse: adding mapping \"%s\"", block->name);
g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), NULL);
} else {
_LOGD ("parse: adding mapping \"%s\" (have connection %s)", block->name,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+ nm_settings_storage_get_uuid (sd->storage));
}
continue;
}
}
/* Make 'auto' interfaces autoconnect=TRUE */
- g_hash_table_iter_init (&con_iter, priv->eni_ifaces);
- while (g_hash_table_iter_next (&con_iter, (gpointer) &block_name, (gpointer) &conn)) {
- NMSettingConnection *setting;
-
- if ( !conn
- || !auto_ifaces
- || !g_hash_table_contains (auto_ifaces, block_name))
- continue;
+ if (auto_ifaces) {
+ g_hash_table_iter_init (&h_iter, priv->eni_ifaces);
+ while (g_hash_table_iter_next (&h_iter, (gpointer *) &block_name, (gpointer *) &sd)) {
+ if ( !sd
+ || !g_hash_table_contains (auto_ifaces, block_name))
+ continue;
+ g_object_set (nm_connection_get_setting_connection (sd->connection),
+ NM_SETTING_CONNECTION_AUTOCONNECT,
+ TRUE,
+ NULL);
+ }
+ }
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- setting = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (conn)));
- g_object_set (setting, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL);
+#if NM_MORE_ASSERTS
+ g_hash_table_iter_init (&h_iter, priv->eni_ifaces);
+ while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &sd)) {
+ if (sd)
+ nmtst_connection_assert_unchanging (sd->connection);
}
+#endif
/* Check the config file to find out whether to manage interfaces */
priv->ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
@@ -281,51 +308,39 @@ initialize (SettingsPluginIfupdown *self)
NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED,
!IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT);
_LOGI ("management mode: %s", priv->ifupdown_managed ? "managed" : "unmanaged");
-
- /* Now if we're running in managed mode, let NM know there are new connections */
- if (priv->ifupdown_managed) {
- GHashTableIter iter;
-
- g_hash_table_iter_init (&iter, priv->eni_ifaces);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &conn)) {
- if (conn) {
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (conn));
- }
- }
- }
}
/*****************************************************************************/
static void
-settings_plugin_ifupdown_init (SettingsPluginIfupdown *self)
+nms_ifupdown_plugin_init (NMSIfupdownPlugin *self)
{
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
- priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+ priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) _storage_data_destroy);
}
static void
dispose (GObject *object)
{
- SettingsPluginIfupdown *plugin = SETTINGS_PLUGIN_IFUPDOWN (object);
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (plugin);
+ NMSIfupdownPlugin *plugin = NMS_IFUPDOWN_PLUGIN (object);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (plugin);
g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
- G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object);
+ G_OBJECT_CLASS (nms_ifupdown_plugin_parent_class)->dispose (object);
}
static void
-settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass)
+nms_ifupdown_plugin_class_init (NMSIfupdownPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
object_class->dispose = dispose;
- plugin_class->get_connections = get_connections;
+ plugin_class->plugin_name = "ifupdown";
+ plugin_class->reload_connections = reload_connections;
plugin_class->get_unmanaged_specs = get_unmanaged_specs;
}
@@ -334,5 +349,5 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass)
G_MODULE_EXPORT NMSettingsPlugin *
nm_settings_plugin_factory (void)
{
- return g_object_new (SETTINGS_TYPE_PLUGIN_IFUPDOWN, NULL);
+ return g_object_new (NMS_TYPE_IFUPDOWN_PLUGIN, NULL);
}
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
index 6e0b74ee6b..10ea2be44e 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
@@ -19,21 +19,21 @@
* (C) Copyright 2008 Canonical Ltd.
*/
-#ifndef _PLUGIN_H_
-#define _PLUGIN_H_
+#ifndef __NMS_IFUPDOWN_PLUGIN_H__
+#define __NMS_IFUPDOWN_PLUGIN_H__
#define PLUGIN_NAME "ifupdown"
-#define SETTINGS_TYPE_PLUGIN_IFUPDOWN (settings_plugin_ifupdown_get_type ())
-#define SETTINGS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdown))
-#define SETTINGS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass))
-#define SETTINGS_IS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN))
-#define SETTINGS_IS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN))
-#define SETTINGS_PLUGIN_IFUPDOWN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass))
+#define NMS_TYPE_IFUPDOWN_PLUGIN (nms_ifupdown_plugin_get_type ())
+#define NMS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin))
+#define NMS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass))
+#define NMS_IS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFUPDOWN_PLUGIN))
+#define NMS_IS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFUPDOWN_PLUGIN))
+#define NMS_IFUPDOWN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass))
-typedef struct _SettingsPluginIfupdown SettingsPluginIfupdown;
-typedef struct _SettingsPluginIfupdownClass SettingsPluginIfupdownClass;
+typedef struct _NMSIfupdownPlugin NMSIfupdownPlugin;
+typedef struct _NMSIfupdownPluginClass NMSIfupdownPluginClass;
-GType settings_plugin_ifupdown_get_type (void);
+GType nms_ifupdown_plugin_get_type (void);
-#endif /* _PLUGIN_H_ */
+#endif /* __NMS_IFUPDOWN_PLUGIN_H__ */
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index 1191047dc2..bad0a6b766 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -30,6 +30,29 @@
/*****************************************************************************/
+#define _connection_from_if_block(block) \
+ ({ \
+ NMConnection *_con; \
+ if_block *_block = (block); \
+ GError *_local = NULL; \
+ \
+ g_assert (_block); \
+ _con = ifupdown_new_connection_from_if_block (_block, &_local); \
+ nmtst_assert_success (NM_IS_CONNECTION (_con), _local); \
+ nmtst_assert_connection_verifies_without_normalization (_con); \
+ _con; \
+ })
+
+#define _connection_first_from_parser(parser) \
+ ({ \
+ if_parser *_parser = (parser); \
+ \
+ g_assert (_parser); \
+ _connection_from_if_block (ifparser_getfirst (_parser)); \
+ })
+
+/*****************************************************************************/
+
typedef struct {
char *key;
char *data;
@@ -452,26 +475,14 @@ test16_missing_newline (void)
static void
test17_read_static_ipv4 (void)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingWired *s_wired;
- GError *error = NULL;
- gboolean success;
NMIPAddress *ip4_addr;
- if_block *block = NULL;
nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test17-wired-static-verify-ip4");
- block = ifparser_getfirst (parser);
- connection = nm_simple_connection_new();
- g_assert (connection);
-
- ifupdown_update_connection_from_if_block (connection, block, &error);
- g_assert_no_error (error);
-
- success = nm_connection_verify (connection, &error);
- g_assert_no_error (error);
- g_assert (success);
+ connection = _connection_first_from_parser (parser);
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
@@ -500,32 +511,19 @@ test17_read_static_ipv4 (void)
g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip4), ==, 2);
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 0), ==, "example.com");
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 1), ==, "foo.example.com");
-
- g_object_unref (connection);
}
static void
test18_read_static_ipv6 (void)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingConnection *s_con;
NMSettingIPConfig *s_ip6;
NMSettingWired *s_wired;
- GError *error = NULL;
- gboolean success;
NMIPAddress *ip6_addr;
- if_block *block = NULL;
nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test18-wired-static-verify-ip6");
- block = ifparser_getfirst (parser);
- connection = nm_simple_connection_new();
- g_assert (connection);
- ifupdown_update_connection_from_if_block (connection, block, &error);
- g_assert_no_error (error);
-
- success = nm_connection_verify (connection, &error);
- g_assert_no_error (error);
- g_assert (success);
+ connection = _connection_first_from_parser (parser);
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
@@ -554,30 +552,17 @@ test18_read_static_ipv6 (void)
g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip6), ==, 2);
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "example.com");
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "foo.example.com");
-
- g_object_unref (connection);
}
static void
test19_read_static_ipv4_plen (void)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingIPConfig *s_ip4;
- GError *error = NULL;
NMIPAddress *ip4_addr;
- if_block *block = NULL;
- gboolean success;
nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test19-wired-static-verify-ip4-plen");
- block = ifparser_getfirst (parser);
- connection = nm_simple_connection_new();
- g_assert (connection);
- ifupdown_update_connection_from_if_block (connection, block, &error);
- g_assert_no_error (error);
-
- success = nm_connection_verify (connection, &error);
- g_assert_no_error (error);
- g_assert (success);
+ connection = _connection_first_from_parser (parser);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
@@ -588,8 +573,6 @@ test19_read_static_ipv4_plen (void)
g_assert (ip4_addr != NULL);
g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "10.0.0.3");
g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 8);
-
- g_object_unref (connection);
}
static void
@@ -670,4 +653,3 @@ main (int argc, char **argv)
return g_test_run ();
}
-
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
deleted file mode 100644
index faf39abbfd..0000000000
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* 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) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nms-keyfile-connection.h"
-
-#include <glib/gstdio.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-setting-connection.h"
-#include "nm-utils.h"
-
-#include "settings/nm-settings-plugin.h"
-
-#include "nms-keyfile-reader.h"
-#include "nms-keyfile-writer.h"
-#include "nms-keyfile-utils.h"
-
-/*****************************************************************************/
-
-struct _NMSKeyfileConnection {
- NMSettingsConnection parent;
-};
-
-struct _NMSKeyfileConnectionClass {
- NMSettingsConnectionClass parent;
-};
-
-G_DEFINE_TYPE (NMSKeyfileConnection, nms_keyfile_connection, NM_TYPE_SETTINGS_CONNECTION)
-
-/*****************************************************************************/
-
-static gboolean
-commit_changes (NMSettingsConnection *connection,
- NMConnection *new_connection,
- NMSettingsConnectionCommitReason commit_reason,
- NMConnection **out_reread_connection,
- char **out_logmsg_change,
- GError **error)
-{
- gs_free char *path = NULL;
- gs_unref_object NMConnection *reread = NULL;
- gboolean reread_same = FALSE;
-
- nm_assert (out_reread_connection && !*out_reread_connection);
- nm_assert (!out_logmsg_change || !*out_logmsg_change);
-
- if (!nms_keyfile_writer_connection (new_connection,
- TRUE,
- nm_settings_connection_get_filename (connection),
- NM_FLAGS_ALL (commit_reason, NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION
- | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED),
- NULL,
- NULL,
- &path,
- &reread,
- &reread_same,
- error))
- return FALSE;
-
- if (!nm_streq0 (path, nm_settings_connection_get_filename (connection))) {
- gs_free char *old_path = g_strdup (nm_settings_connection_get_filename (connection));
-
- nm_settings_connection_set_filename (connection, path);
- if (old_path) {
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"",
- NMS_KEYFILE_CONNECTION_LOG_ARG (connection),
- old_path));
- } else {
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection",
- NMS_KEYFILE_CONNECTION_LOG_ARG (connection)));
- }
- } else {
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT,
- NMS_KEYFILE_CONNECTION_LOG_ARG (connection)));
- }
-
- if (reread && !reread_same)
- *out_reread_connection = g_steal_pointer (&reread);
-
- return TRUE;
-}
-
-static gboolean
-delete (NMSettingsConnection *connection,
- GError **error)
-{
- const char *path;
-
- path = nm_settings_connection_get_filename (connection);
- if (path)
- g_unlink (path);
- return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-nms_keyfile_connection_init (NMSKeyfileConnection *connection)
-{
-}
-
-NMSKeyfileConnection *
-nms_keyfile_connection_new (NMConnection *source,
- const char *full_path,
- const char *profile_dir,
- GError **error)
-{
- GObject *object;
- NMConnection *tmp;
- const char *uuid;
- gboolean update_unsaved = TRUE;
-
- nm_assert (source || full_path);
- nm_assert (!full_path || full_path[0] == '/');
- nm_assert (!profile_dir || profile_dir[0] == '/');
-
- /* If we're given a connection already, prefer that instead of re-reading */
- if (source)
- tmp = g_object_ref (source);
- else {
- tmp = nms_keyfile_reader_from_file (full_path, profile_dir, error);
- if (!tmp)
- return NULL;
-
- uuid = nm_connection_get_uuid (tmp);
- if (!uuid) {
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
- "Connection in file %s had no UUID", full_path);
- g_object_unref (tmp);
- return NULL;
- }
-
- /* If we just read the connection from disk, it's clearly not Unsaved */
- update_unsaved = FALSE;
- }
-
- object = g_object_new (NMS_TYPE_KEYFILE_CONNECTION,
- NM_SETTINGS_CONNECTION_FILENAME, full_path,
- NULL);
-
- /* Update our settings with what was read from the file */
- if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
- tmp,
- update_unsaved
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- NULL,
- error)) {
- g_object_unref (object);
- object = NULL;
- }
-
- g_object_unref (tmp);
- return (NMSKeyfileConnection *) object;
-}
-
-static void
-nms_keyfile_connection_class_init (NMSKeyfileConnectionClass *keyfile_connection_class)
-{
- NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (keyfile_connection_class);
-
- settings_class->commit_changes = commit_changes;
- settings_class->delete = delete;
-}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.h b/src/settings/plugins/keyfile/nms-keyfile-connection.h
deleted file mode 100644
index 2e7a59d910..0000000000
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* 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) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
- */
-
-#ifndef __NMS_KEYFILE_CONNECTION_H__
-#define __NMS_KEYFILE_CONNECTION_H__
-
-#include "settings/nm-settings-connection.h"
-
-#define NMS_TYPE_KEYFILE_CONNECTION (nms_keyfile_connection_get_type ())
-#define NMS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnection))
-#define NMS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass))
-#define NMS_IS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_CONNECTION))
-#define NMS_IS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_CONNECTION))
-#define NMS_KEYFILE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass))
-
-typedef struct _NMSKeyfileConnection NMSKeyfileConnection;
-typedef struct _NMSKeyfileConnectionClass NMSKeyfileConnectionClass;
-
-GType nms_keyfile_connection_get_type (void);
-
-NMSKeyfileConnection *nms_keyfile_connection_new (NMConnection *source,
- const char *full_path,
- const char *profile_dir,
- GError **error);
-
-#endif /* __NMS_KEYFILE_CONNECTION_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index 46432fd257..3b9435440b 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -15,7 +15,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2013 Red Hat, Inc.
+ * Copyright (C) 2008 - 2018 Red Hat, Inc.
*/
#include "nm-default.h"
@@ -27,6 +27,10 @@
#include <sys/types.h>
#include <glib/gstdio.h>
+#include "nm-std-aux/c-list-util.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-glib-aux/nm-io-utils.h"
+
#include "nm-connection.h"
#include "nm-setting.h"
#include "nm-setting-connection.h"
@@ -35,20 +39,76 @@
#include "nm-core-internal.h"
#include "nm-keyfile-internal.h"
+#include "systemd/nm-sd-utils-shared.h"
+
#include "settings/nm-settings-plugin.h"
+#include "settings/nm-settings-storage.h"
-#include "nms-keyfile-connection.h"
+#include "nms-keyfile-storage.h"
#include "nms-keyfile-writer.h"
+#include "nms-keyfile-reader.h"
#include "nms-keyfile-utils.h"
/*****************************************************************************/
typedef struct {
- GHashTable *connections; /* uuid::connection */
+ 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;
- gboolean initialized;
+typedef struct {
NMConfig *config;
+
+ /* there can/could be multiple read-only directories. For example, one
+ * could set dirname_libs to
+ * - /usr/lib/NetworkManager/profiles/
+ * - /etc/NetworkManager/system-connections
+ * and leave dirname_etc unset. In this case, there would be multiple
+ * read-only directories.
+ *
+ * Directories that come later have higher priority and shadow profiles
+ * from earlier directories.
+ *
+ * Currently, this is only an array with zero or one elements. It could be
+ * easily extended to support multiple read-only directories.
+ */
+ char *dirname_libs[2];
+ char *dirname_etc;
+ char *dirname_run;
+
+ struct {
+ CList lst_head;
+ GHashTable *idx;
+ GHashTable *filename_idx;
+ } storages;
+
} NMSKeyfilePluginPrivate;
struct _NMSKeyfilePlugin {
@@ -62,7 +122,7 @@ struct _NMSKeyfilePluginClass {
G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN)
-#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN)
+#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN, NMSettingsPlugin)
/*****************************************************************************/
@@ -76,430 +136,1241 @@ G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN)
/*****************************************************************************/
-static void
-connection_removed_cb (NMSettingsConnection *sett_conn, NMSKeyfilePlugin *self)
+static void _storage_reload_data_head_clear (NMSKeyfileStorage *storage);
+
+/*****************************************************************************/
+
+static gboolean
+_ignore_filename (NMSKeyfileStorageType storage_type,
+ const char *filename)
{
- g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections,
- nm_settings_connection_get_uuid (sett_conn));
+ /* for backward-compatibility, we don't require an extension for
+ * files under "/etc/...". */
+ return nm_keyfile_utils_ignore_filename (filename,
+ (storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC));
}
-/* Monitoring */
+static const char *
+_get_plugin_dir (NMSKeyfilePluginPrivate *priv)
+{
+ /* the plugin dir is only needed to generate connection.uuid value via
+ * nm_keyfile_read_ensure_uuid(). This is either the configured /etc
+ * directory, of the compile-time default (in case the /etc directory
+ * is disabled). */
+ return priv->dirname_etc ?: NM_KEYFILE_PATH_NAME_ETC_DEFAULT;
+}
-static void
-remove_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection)
+static gboolean
+_path_detect_storage_type (const char *full_filename,
+ const char *const*dirname_libs,
+ const char *dirname_etc,
+ const char *dirname_run,
+ NMSKeyfileStorageType *out_storage_type,
+ const char **out_dirname,
+ const char **out_filename,
+ GError **error)
{
- gboolean removed;
+ NMSKeyfileStorageType storage_type;
+ const char *filename = NULL;
+ const char *dirname = NULL;
+ guint i;
- g_return_if_fail (connection != NULL);
+ if (full_filename[0] != '/') {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+ "filename is not an absolute path");
+ return FALSE;
+ }
- _LOGI ("removed " NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection));
+ if ( dirname_run
+ && (filename = nm_utils_file_is_in_path (full_filename, dirname_run))) {
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN;
+ dirname = dirname_run;
+ } else if ( dirname_etc
+ && (filename = nm_utils_file_is_in_path (full_filename, dirname_etc))) {
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC;
+ dirname = dirname_etc;
+ } 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;
+ dirname = dirname_libs[i];
+ break;
+ }
+ }
+ if (!dirname) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+ "filename is not inside a keyfile directory");
+ return FALSE;
+ }
+ }
- /* Removing from the hash table should drop the last reference */
- g_object_ref (connection);
- g_signal_handlers_disconnect_by_func (connection, connection_removed_cb, self);
- removed = g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection)));
- nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
- g_object_unref (connection);
+ if (_ignore_filename (storage_type, filename)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+ "filename is not a valid keyfile");
+ return FALSE;
+ }
- g_return_if_fail (removed);
+ NM_SET_OUT (out_storage_type, storage_type);
+ NM_SET_OUT (out_dirname, dirname);
+ NM_SET_OUT (out_filename, filename);
+ return TRUE;
}
-static NMSKeyfileConnection *
-find_by_path (NMSKeyfilePlugin *self, const char *path)
+/*****************************************************************************/
+
+static NMConnection *
+_read_from_file (const char *full_filename,
+ const char *plugin_dir,
+ struct stat *out_stat,
+ NMTernary *out_is_nm_generated,
+ NMTernary *out_is_volatile,
+ GError **error)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- GHashTableIter iter;
- NMSettingsConnection *candidate = NULL;
+ NMConnection *connection;
+
+ nm_assert (full_filename && full_filename[0] == '/');
+
+ connection = nms_keyfile_reader_from_file (full_filename, plugin_dir, out_stat, out_is_nm_generated, out_is_volatile, error);
+
+ nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
+ nm_assert (!connection || nm_utils_is_uuid (nm_connection_get_uuid (connection)));
+
+ return connection;
+}
+
+/*****************************************************************************/
- g_return_val_if_fail (path != NULL, NULL);
+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);
+}
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
- if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0)
- return NMS_KEYFILE_CONNECTION (candidate);
+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;
}
- return NULL;
-}
-
-/* update_connection:
- * @self: the plugin instance
- * @source: if %NULL, this re-reads the connection from @full_path
- * and updates it. When passing @source, this adds a connection from
- * memory.
- * @full_path: the filename of the keyfile to be loaded
- * @connection: an existing connection that might be updated.
- * If given, @connection must be an existing connection that is currently
- * owned by the plugin.
- * @protect_existing_connection: if %TRUE, and !@connection, we don't allow updating
- * an existing connection with the same UUID.
- * If %TRUE and @connection, allow updating only if the reload would modify
- * @connection (without changing its UUID) or if we would create a new connection.
- * In other words, if this parameter is %TRUE, we only allow creating a
- * new connection (with an unseen UUID) or updating the passed in @connection
- * (whereas the UUID cannot change).
- * Note, that this allows for @connection to be replaced by a new connection.
- * @protected_connections: (allow-none): if given, we only update an
- * existing connection if it is not contained in this hash.
- * @error: error in case of failure
- *
- * Loads a connection from file @full_path. This can both be used to
- * load a connection initially or to update an existing connection.
- *
- * If you pass in an existing connection and the reloaded file happens
- * to have a different UUID, the connection is deleted.
- * Beware, that means that after the function, you have a dangling pointer
- * if the returned connection is different from @connection.
- *
- * Returns: the updated connection.
- * */
-static NMSKeyfileConnection *
-update_connection (NMSKeyfilePlugin *self,
- NMConnection *source,
- const char *full_path,
- NMSKeyfileConnection *connection,
- gboolean protect_existing_connection,
- GHashTable *protected_connections,
- GError **error)
+
+ 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)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- NMSKeyfileConnection *connection_new;
- NMSKeyfileConnection *connection_by_uuid;
- GError *local = NULL;
- const char *uuid;
+ const ConnReloadData *a = c_list_entry (lst_a, ConnReloadData, crld_lst);
+ const ConnReloadData *b = c_list_entry (lst_b, ConnReloadData, crld_lst);
+
+ /* we sort more important entries first. */
+
+ /* 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);
- g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL);
- g_return_val_if_fail (full_path || source, NULL);
+ /* newer files are more important. */
+ NM_CMP_FIELD (b, a, stat_mtime.tv_sec);
+ NM_CMP_FIELD (b, a, stat_mtime.tv_nsec);
- if (full_path)
- _LOGD ("loading from file \"%s\"...", full_path);
+ NM_CMP_FIELD_STR (a, b, filename);
- if ( !nm_utils_file_is_in_path (full_path, nms_keyfile_utils_get_path ())
- && !nm_utils_file_is_in_path (full_path, NM_KEYFILE_PATH_NAME_RUN)) {
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "File not in recognized system-connections directory");
+ 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;
- }
- connection_new = nms_keyfile_connection_new (source, full_path, nms_keyfile_utils_get_path (), &local);
- if (!connection_new) {
- /* Error; remove the connection */
- if (source)
- _LOGW ("error creating connection %s: %s", nm_connection_get_uuid (source), local->message);
- else
- _LOGW ("error loading connection from file %s: %s", full_path, local->message);
- if ( connection
- && !protect_existing_connection
- && (!protected_connections || !g_hash_table_contains (protected_connections, connection)))
- remove_connection (self, connection);
- g_propagate_error (error, local);
- return NULL;
+ /* 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;
+ }
}
- uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new));
- connection_by_uuid = g_hash_table_lookup (priv->connections, uuid);
+ return FALSE;
+}
- if ( connection
- && connection != connection_by_uuid) {
+/*****************************************************************************/
- if ( (protect_existing_connection && connection_by_uuid != NULL)
- || (protected_connections && g_hash_table_contains (protected_connections, connection))) {
- NMSKeyfileConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection;
+static void
+_nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */,
+ gpointer storage /* NMSKeyfileStorage */,
+ gboolean tracked)
+{
+#if NM_MORE_ASSERTS
+ 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 (({
+ const char *f = nms_keyfile_storage_get_filename (storage);
+ !f || f[0] == '/';
+ }));
+ nm_assert (nm_utils_is_uuid (nms_keyfile_storage_get_uuid (storage)));
+
+ nm_assert ( !tracked
+ || !plugin
+ || c_list_contains (&NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.lst_head,
+ &NMS_KEYFILE_STORAGE (storage)->storage_lst));
+
+ nm_assert ( !tracked
+ || !plugin
+ || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx,
+ nms_keyfile_storage_get_uuid (storage)));
+#endif
+}
- if (source)
- _LOGW ("cannot update protected "NMS_KEYFILE_CONNECTION_LOG_FMT" connection due to conflicting UUID %s", NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting), uuid);
- else
- _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Cannot update protected connection due to conflicting UUID");
- return NULL;
- }
+void
+_nms_keyfile_storage_clear (NMSKeyfileStorage *storage)
+{
+ _nm_assert_storage (NULL, storage, FALSE);
- /* The new connection has a different UUID then the original one.
- * Remove @connection. */
- remove_connection (self, connection);
- }
+ c_list_unlink (&storage->storage_lst);
- if ( connection_by_uuid
- && ( (!connection && protect_existing_connection)
- || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) {
- if (source)
- _LOGW ("cannot update connection due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid));
- else
- _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Skip updating protected connection during reload");
- return NULL;
- }
+ _storage_reload_data_head_clear (storage);
- if (connection_by_uuid) {
- const char *old_path;
-
- old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid));
-
- if (nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
- NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
- /* Nothing to do... except updating the path. */
- if (old_path && g_strcmp0 (old_path, full_path) != 0)
- _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT" without other changes", old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- } else {
- /* An existing connection changed. */
- if (source)
- _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT" from %s", NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new), NMS_KEYFILE_CONNECTION_LOG_PATH (old_path));
- else if (!g_strcmp0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new))))
- _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- else if (old_path)
- _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT, old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("update and persist "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
-
- if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "keyfile-update",
- &local)) {
- /* Shouldn't ever get here as 'connection_new' was verified by the reader already
- * and the UUID did not change. */
- g_assert_not_reached ();
- }
- g_assert_no_error (local);
- }
- nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path);
- g_object_unref (connection_new);
- return connection_by_uuid;
- } else {
- if (source)
- _LOGI ("add connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("new connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- g_hash_table_insert (priv->connections, g_strdup (uuid), connection_new);
-
- g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED,
- G_CALLBACK (connection_removed_cb),
- self);
-
- if (!source) {
- /* Only raise the signal if we were called without source, i.e. if we read the connection from file.
- * Otherwise, we were called by add_connection() which does not expect the signal. */
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_new));
- }
+ g_clear_object (&storage->connection_exported);
+}
+
+static void
+_storage_destroy (NMSKeyfileStorage *storage)
+{
+ _nm_assert_storage (NULL, storage, TRUE);
- return connection_new;
+ _nms_keyfile_storage_clear (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
-config_changed_cb (NMConfig *config,
- NMConfigData *config_data,
- NMConfigChangeFlags changes,
- NMConfigData *old_data,
- NMSKeyfilePlugin *self)
+_storage_reload_data_head_clear (NMSKeyfileStorage *storage)
{
- gs_free char *old_value = NULL;
- gs_free char *new_value = NULL;
+ ConnReloadHead *hd;
+ ConnReloadData *storage_data;
- old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
- new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ hd = g_steal_pointer (&storage->_reload_data_head);
+ if (!hd)
+ return;
- if (!nm_streq0 (old_value, new_value))
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
+ 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);
}
-static GHashTable *
-_paths_from_connections (GHashTable *connections)
+static gboolean
+_storage_has_equal_connection (NMSKeyfileStorage *storage,
+ NMConnection *connection)
{
- GHashTableIter iter;
- NMSKeyfileConnection *connection;
- GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal);
+ nm_assert (NM_IS_CONNECTION (connection));
- g_hash_table_iter_init (&iter, connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection));
+ return storage->connection_exported
+ && nm_connection_compare (connection,
+ storage->connection_exported,
+ NM_SETTING_COMPARE_FLAG_EXACT);
+}
- if (path)
- g_hash_table_add (paths, (void *) path);
+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 paths;
}
-static int
-_sort_paths (const char **f1, const char **f2, GHashTable *paths)
+/*****************************************************************************/
+
+typedef struct {
+ GHashTable *filename_idx;
+ const char *allowed_filename;
+} AllowFilenameData;
+
+#define ALLOW_FILENAME_DATA(_filename_idx, _allowed_filename) \
+ (&((AllowFilenameData) { \
+ .filename_idx = (_filename_idx), \
+ .allowed_filename = (_allowed_filename), \
+ }))
+
+static gboolean
+_allow_filename_cb (const char *filename,
+ gpointer user_data)
{
- struct stat st;
- gboolean c1, c2;
- gint64 m1, m2;
+ 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;
+}
+
+static NMSKeyfileStorage *
+_storages_get (NMSKeyfilePlugin *self,
+ const char *uuid,
+ gboolean create)
+{
+ 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;
+}
+
+static void
+_storages_set_full_filename (NMSKeyfilePluginPrivate *priv,
+ NMSKeyfileStorage *storage,
+ char *full_filename_take)
+{
+ const char *filename;
+
+ _nm_assert_storage (NULL, storage, TRUE);
+ nm_assert (full_filename_take && full_filename_take[0] == '/');
+
+ filename = nms_keyfile_storage_get_filename (storage);
+ if (filename) {
+ if (!g_hash_table_remove (priv->storages.filename_idx, filename))
+ nm_assert_not_reached ();
+ }
+
+ _nm_settings_storage_set_filename_take (NM_SETTINGS_STORAGE (storage), full_filename_take);
+
+ if (!g_hash_table_insert (priv->storages.filename_idx,
+ (char *) nms_keyfile_storage_get_filename (storage),
+ storage))
+ nm_assert_not_reached ();
+}
+
+static void
+_storages_remove (NMSKeyfilePluginPrivate *priv,
+ NMSKeyfileStorage *storage,
+ gboolean destroy /* or else steal */)
+{
+ 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);
- c1 = !!g_hash_table_contains (paths, *f1);
- c2 = !!g_hash_table_contains (paths, *f2);
- if (c1 != c2)
- return c1 ? -1 : 1;
+ c_list_unlink (&storage->storage_lst);
- m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- if (m1 != m2)
- return m1 > m2 ? -1 : 1;
+ filename = nms_keyfile_storage_get_filename (storage);
+ if (filename) {
+ if (!g_hash_table_remove (priv->storages.filename_idx, filename))
+ nm_assert_not_reached ();
+
+ /* 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. */
+ }
- return strcmp (*f1, *f2);
+ 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 ();
+ }
}
+/*****************************************************************************/
+
static void
-_read_dir (GPtrArray *filenames,
- const char *path,
- gboolean require_extension)
+_load_dir (NMSKeyfilePlugin *self,
+ guint storage_priority,
+ NMSKeyfileStorageType storage_type,
+ const char *dirname,
+ const char *plugin_dir)
{
+ const char *filename;
GDir *dir;
- const char *item;
- GError *error = NULL;
+ gs_unref_hashtable GHashTable *dupl_filenames = NULL;
- dir = g_dir_open (path, 0, &error);
- if (!dir) {
- _LOGD ("cannot read directory '%s': %s", path, error->message);
- g_clear_error (&error);
+ if (!dirname)
return;
- }
- while ((item = g_dir_read_name (dir))) {
- if (nm_keyfile_utils_ignore_filename (item, require_extension))
+ 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);
+
+ 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;
+
+ 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);
+ }
+ 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);
continue;
- g_ptr_array_add (filenames, g_build_filename (path, item, NULL));
+ }
+
+ 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);
}
+
g_dir_close (dir);
}
+/*****************************************************************************/
static void
-read_connections (NMSettingsPlugin *config)
+reload_connections (NMSettingsPlugin *plugin,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
{
- NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
+ NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin);
NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- GHashTable *alive_connections;
- GHashTableIter iter;
- NMSKeyfileConnection *connection;
- GPtrArray *dead_connections = NULL;
+ 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;
+ NMSKeyfileStorage *storage_safe;
+ NMSKeyfileStorage *storage;
guint i;
- GPtrArray *filenames;
- GHashTable *paths;
- filenames = g_ptr_array_new_with_free_func (g_free);
+#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);
+
+ _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);
+
+ /* 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);
+ }
+ }
- _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE);
- _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE);
+ 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)" : "");
+ }
+ }
- alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+ 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);
+ }
+ continue;
+ }
- /* While reloading, we don't replace connections that we already loaded while
- * iterating over the files.
- *
- * To have sensible, reproducible behavior, sort the paths by last modification
- * time preferring older files.
- */
- paths = _paths_from_connections (priv->connections);
- g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths);
- g_hash_table_destroy (paths);
-
- for (i = 0; i < filenames->len; i++) {
- connection = update_connection (self, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL);
- if (connection)
- g_hash_table_add (alive_connections, connection);
- }
- g_ptr_array_free (filenames, TRUE);
-
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- if ( !g_hash_table_contains (alive_connections, connection)
- && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) {
- if (!dead_connections)
- dead_connections = g_ptr_array_new ();
- g_ptr_array_add (dead_connections, connection);
+ 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);
+ }
+ }
+
+ 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);
+
+ if (connection_modified)
+ nm_g_object_ref_set (&storage->connection_exported, rd_best->connection);
+
+ _storages_set_full_filename (priv,
+ storage,
+ g_steal_pointer (&rd_best->full_filename));
+
+ /* we don't need the reload data anymore. Drop it. */
+ _storage_reload_data_head_clear (storage);
+
+ g_ptr_array_add (storages_modified, g_object_ref (storage));
}
- g_hash_table_destroy (alive_connections);
- if (dead_connections) {
- for (i = 0; i < dead_connections->len; i++)
- remove_connection (self, dead_connections->pdata[i]);
- g_ptr_array_free (dead_connections, TRUE);
+ /* raise events. */
+
+ 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);
}
-}
-/*****************************************************************************/
+ for (i = 0; i < storages_modified->len; i++) {
+ storage = storages_modified->pdata[i];
-static GSList *
-get_connections (NMSettingsPlugin *config)
-{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config);
+ if (storage != _storages_get (self, nms_keyfile_storage_get_uuid (storage), FALSE)) {
+ /* 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 (!priv->initialized) {
- read_connections (config);
- priv->initialized = TRUE;
+ nm_assert (NM_IS_CONNECTION (storage->connection_exported));
+
+ callback (NM_SETTINGS_PLUGIN (self),
+ NM_SETTINGS_STORAGE (storage),
+ storage->connection_exported,
+ user_data);
}
- return _nm_utils_hash_values_to_slist (priv->connections);
}
static gboolean
-load_connection (NMSettingsPlugin *config,
- const char *filename)
-{
- NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN ((NMSKeyfilePlugin *) config);
- NMSKeyfileConnection *connection;
- gboolean require_extension;
-
- if (nm_utils_file_is_in_path (filename, nms_keyfile_utils_get_path ()))
- require_extension = FALSE;
- else if (nm_utils_file_is_in_path (filename, NM_KEYFILE_PATH_NAME_RUN))
- require_extension = TRUE;
- else
+load_connection (NMSettingsPlugin *plugin,
+ const char *full_filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error)
+{
+ 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_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;
- if (nm_keyfile_utils_ignore_filename (filename, require_extension))
+ storage_by_filename = g_hash_table_lookup (priv->storages.filename_idx, full_filename);
+
+ connection_new = _read_from_file (full_filename, _get_plugin_dir (priv), NULL, &is_nm_generated_opt, &is_volatile_opt, &local);
+
+ if (!connection_new) {
+ struct stat st;
+
+ if ( storage_by_filename
+ && stat (full_filename, &st) != 0
+ && errno == ENOENT) {
+
+ /* 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. */
+
+ *out_storage_replaced = g_object_ref (NM_SETTINGS_STORAGE (storage_by_filename));
+
+ _LOGT ("load: \"%s\": unload previously loaded connection %s (file no longer exists)",
+ full_filename,
+ nms_keyfile_storage_get_uuid (storage_by_filename));
+
+ _storages_remove (priv, storage_by_filename, TRUE);
+
+ /* Despite we did not *load* anything, we successfully *unloaded*. Return success. */
+ return TRUE;
+ }
+
+ _LOGT ("load: \"%s\": failed to load connection: %s", full_filename, local->message);
+ g_propagate_error (error, g_steal_pointer (&local));
return FALSE;
+ }
- connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL);
+ uuid = nm_connection_get_uuid (connection_new);
+
+ 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));
+ }
- return (connection != NULL);
+ if (connection_modified)
+ nm_g_object_ref_set (&storage->connection_exported, connection_new);
+
+ *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));
+
+ return TRUE;
}
-static void
-reload_connections (NMSettingsPlugin *config)
+gboolean
+nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean in_memory,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
{
- read_connections (config);
+ 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;
+ GError *local = NULL;
+ const char *uuid;
+
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (out_storage && !*out_storage);
+ nm_assert (out_connection && !*out_connection);
+
+ 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;
+
+ if (!nms_keyfile_writer_connection (connection,
+ is_nm_generated,
+ is_volatile,
+ storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC
+ ? priv->dirname_etc
+ : priv->dirname_run,
+ _get_plugin_dir (priv),
+ NULL,
+ FALSE,
+ FALSE,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, NULL),
+ &full_filename,
+ &reread,
+ NULL,
+ &local)) {
+ _LOGT ("add: %s, \"%s\": failed to add connection: %s",
+ nm_connection_get_uuid (connection),
+ nm_connection_get_id (connection),
+ local->message);
+ g_propagate_error (error, local);
+ return FALSE;
+ }
+
+ 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));
+
+ 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,
+ 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);
+
+ storage->connection_exported = g_object_ref (reread);
+
+ _storages_set_full_filename (priv,
+ storage,
+ g_steal_pointer (&full_filename));
+
+ *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage));
+ *out_connection = g_steal_pointer (&reread);
+
+ return TRUE;
}
-static NMSettingsConnection *
-add_connection (NMSettingsPlugin *config,
+static gboolean
+add_connection (NMSettingsPlugin *plugin,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
GError **error)
{
- NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
- gs_free char *path = NULL;
+ return nms_keyfile_plugin_add_connection (NMS_KEYFILE_PLUGIN (plugin),
+ connection,
+ FALSE,
+ FALSE,
+ FALSE,
+ out_storage,
+ out_connection,
+ error);
+}
+
+gboolean
+nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
+ NMSettingsStorage *storage_x,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean force_rename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (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;
+
+ _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_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;
+ }
+
+ previous_filename = nms_keyfile_storage_get_filename (storage);
+ uuid = nms_keyfile_storage_get_uuid (storage);
if (!nms_keyfile_writer_connection (connection,
- save_to_disk,
- NULL,
- FALSE,
- NULL,
- NULL,
- &path,
+ is_nm_generated,
+ is_volatile,
+ 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,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, previous_filename),
+ &full_filename,
&reread,
NULL,
- error))
- return NULL;
+ &local)) {
+ _LOGW ("commit: failure to write %s (%s) to disk: %s",
+ uuid,
+ nm_connection_get_id (connection_clone),
+ local->message);
+ g_propagate_error (error, local);
+ return FALSE;
+ }
+
+ loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run,
+ uuid,
+ full_filename,
+ TRUE,
+ &loaded_uuid_filename);
+
+ connection_modified = !_storage_has_equal_connection (storage, connection);
+ connection_renamed = !nm_streq (previous_filename, full_filename);
+
+ _LOGT ("commit: \"%s\": profile %s (%s) written%s"
+ "%s%s%s"
+ " (%s%s%s)",
+ 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));
+ }
+
+ if (connection_modified)
+ nm_g_object_ref_set (&storage->connection_exported, connection);
+
+ *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage));
+ *out_connection = g_object_ref (storage->connection_exported);
+ return TRUE;
+}
+
+static gboolean
+update_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ return nms_keyfile_plugin_update_connection (NMS_KEYFILE_PLUGIN (plugin),
+ storage,
+ connection,
+ FALSE,
+ FALSE,
+ FALSE,
+ out_storage,
+ out_connection,
+ error);
+}
+
+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;
+
+ _nm_assert_storage (self, storage, TRUE);
+ nm_assert (!error || !*error);
+
+ 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))
+ operation_message = "dropped readonly file from memory";
+ 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";
+ } 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)",
+ previous_filename,
+ 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"));
+
+ _storages_remove (priv, storage, TRUE);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+config_changed_cb (NMConfig *config,
+ NMConfigData *config_data,
+ NMConfigChangeFlags changes,
+ NMConfigData *old_data,
+ NMSKeyfilePlugin *self)
+{
+ gs_free char *old_value = NULL;
+ gs_free char *new_value = NULL;
- return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
+ old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+
+ if (!nm_streq0 (old_value, new_value))
+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
}
static GSList *
get_unmanaged_specs (NMSettingsPlugin *config)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (config);
gs_free char *value = NULL;
value = nm_config_data_get_value (nm_config_get_data (priv->config),
@@ -517,7 +1388,46 @@ nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin)
NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin);
priv->config = g_object_ref (nm_config_get ());
- priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+
+ 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);
+
+ /* 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
+ * is a list (which currently only has at most one directory). */
+ priv->dirname_libs[0] = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_LIB), FALSE);
+ priv->dirname_libs[1] = NULL;
+ priv->dirname_run = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_RUN), FALSE);
+ priv->dirname_etc = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
+ NM_CONFIG_KEYFILE_GROUP_KEYFILE,
+ NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
+ NM_CONFIG_GET_VALUE_STRIP);
+ if (priv->dirname_etc && priv->dirname_etc[0] == '\0') {
+ /* special case: configure an empty keyfile path so that NM has no writable keyfile
+ * directory. In this case, NM will only honor dirname_libs and dirname_run, meaning
+ * it cannot persist profile to non-volatile memory. */
+ nm_clear_g_free (&priv->dirname_etc);
+ } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') {
+ /* either invalid path or unspecified. Use the default. */
+ g_free (priv->dirname_etc);
+ priv->dirname_etc = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE);
+ } else
+ nm_sd_utils_path_simplify (priv->dirname_etc, FALSE);
+
+ /* no duplicates */
+ if (NM_IN_STRSET (priv->dirname_libs[0], priv->dirname_etc,
+ priv->dirname_run))
+ nm_clear_g_free (&priv->dirname_libs[0]);
+ if (NM_IN_STRSET (priv->dirname_etc, priv->dirname_run))
+ nm_clear_g_free (&priv->dirname_etc);
+
+ nm_assert (!priv->dirname_libs[0] || priv->dirname_libs[0][0] == '/');
+ nm_assert (!priv->dirname_etc || priv->dirname_etc[0] == '/');
+ nm_assert ( priv->dirname_run && priv->dirname_run[0] == '/');
}
static void
@@ -555,17 +1465,20 @@ nms_keyfile_plugin_new (void)
static void
dispose (GObject *object)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object);
-
- if (priv->connections) {
- g_hash_table_destroy (priv->connections);
- priv->connections = NULL;
- }
+ NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- if (priv->config) {
+ if (priv->config)
g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object);
- g_clear_object (&priv->config);
- }
+
+ nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy);
+ nm_clear_pointer (&priv->storages.idx, g_hash_table_destroy);
+
+ nm_clear_g_free (&priv->dirname_libs[0]);
+ nm_clear_g_free (&priv->dirname_etc);
+ nm_clear_g_free (&priv->dirname_run);
+
+ g_clear_object (&priv->config);
G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->dispose (object);
}
@@ -579,9 +1492,11 @@ nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *klass)
object_class->constructed = constructed;
object_class->dispose = dispose;
- plugin_class->get_connections = get_connections;
- plugin_class->load_connection = load_connection;
+ 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->add_connection = add_connection;
- plugin_class->get_unmanaged_specs = get_unmanaged_specs;
+ 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 95403c779b..7d5b762285 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
@@ -21,6 +21,9 @@
#ifndef __NMS_KEYFILE_PLUGIN_H__
#define __NMS_KEYFILE_PLUGIN_H__
+#include "settings/nm-settings-plugin.h"
+#include "settings/nm-settings-storage.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))
@@ -35,4 +38,23 @@ GType nms_keyfile_plugin_get_type (void);
NMSKeyfilePlugin *nms_keyfile_plugin_new (void);
+gboolean nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean in_memory,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+gboolean nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean force_rename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
#endif /* __NMS_KEYFILE_PLUGIN_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 76b4828b42..a6562a04dc 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -161,6 +161,9 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
NMConnection *
nms_keyfile_reader_from_file (const char *full_filename,
const char *profile_dir,
+ struct stat *out_stat,
+ NMTernary *out_is_nm_generated,
+ NMTernary *out_is_volatile,
GError **error)
{
gs_unref_keyfile GKeyFile *key_file = NULL;
@@ -170,9 +173,12 @@ nms_keyfile_reader_from_file (const char *full_filename,
nm_assert (full_filename && full_filename[0] == '/');
nm_assert (!profile_dir || profile_dir[0] == '/');
+ NM_SET_OUT (out_is_nm_generated, NM_TERNARY_DEFAULT);
+ NM_SET_OUT (out_is_volatile, NM_TERNARY_DEFAULT);
+
if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE,
full_filename,
- NULL,
+ out_stat,
error))
return NULL;
@@ -194,6 +200,16 @@ nms_keyfile_reader_from_file (const char *full_filename,
connection = NULL;
}
+ NM_SET_OUT (out_is_nm_generated, nm_key_file_get_boolean (key_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_NM_GENERATED,
+ NM_TERNARY_DEFAULT));
+
+ NM_SET_OUT (out_is_volatile, nm_key_file_get_boolean (key_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_VOLATILE,
+ NM_TERNARY_DEFAULT));
+
return connection;
}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index 430096ebb7..b17b6dd77b 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -30,8 +30,13 @@ NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
gboolean verbose,
GError **error);
+struct stat;
+
NMConnection *nms_keyfile_reader_from_file (const char *full_filename,
const char *profile_dir,
+ struct stat *out_stat,
+ NMTernary *out_is_nm_generated,
+ NMTernary *out_is_volatile,
GError **error);
#endif /* __NMS_KEYFILE_READER_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c
new file mode 100644
index 0000000000..b3786c1286
--- /dev/null
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c
@@ -0,0 +1,101 @@
+/* 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) 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-keyfile-storage.h"
+
+#include "nm-utils.h"
+#include "nms-keyfile-plugin.h"
+
+/*****************************************************************************/
+
+struct _NMSKeyfileStorageClass {
+ NMSettingsStorageClass parent;
+};
+
+G_DEFINE_TYPE (NMSKeyfileStorage, nms_keyfile_storage, NM_TYPE_SETTINGS_STORAGE)
+
+/*****************************************************************************/
+
+static void
+nms_keyfile_storage_init (NMSKeyfileStorage *self)
+{
+ c_list_init (&self->storage_lst);
+}
+
+NMSKeyfileStorage *
+nms_keyfile_storage_new (NMSKeyfilePlugin *plugin,
+ const char *uuid)
+{
+ nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin));
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ return g_object_new (NMS_TYPE_KEYFILE_STORAGE,
+ NM_SETTINGS_STORAGE_PLUGIN, plugin,
+ NM_SETTINGS_STORAGE_UUID, uuid,
+ NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMSKeyfileStorage *self = NMS_KEYFILE_STORAGE (object);
+
+ _nms_keyfile_storage_clear (self);
+
+ G_OBJECT_CLASS (nms_keyfile_storage_parent_class)->dispose (object);
+}
+
+static void
+nms_keyfile_storage_class_init (NMSKeyfileStorageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dispose;
+}
+
+/*****************************************************************************/
+
+#include "settings/nm-settings-connection.h"
+
+void
+nm_settings_storage_load_sett_flags (NMSettingsStorage *self,
+ NMSettingsConnectionIntFlags *sett_flags,
+ NMSettingsConnectionIntFlags *sett_mask)
+{
+ NMSKeyfileStorage *s;
+
+ *sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+ *sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+
+ if (!NMS_IS_KEYFILE_STORAGE (self))
+ return;
+
+ s = NMS_KEYFILE_STORAGE (self);
+ if (s->storage_type_exported != NMS_KEYFILE_STORAGE_TYPE_RUN)
+ return;
+
+ if (s->is_nm_generated)
+ *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED;
+
+ if (s->is_volatile)
+ *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h
new file mode 100644
index 0000000000..0f0e7fdc70
--- /dev/null
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h
@@ -0,0 +1,118 @@
+/* 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) 2018 Red Hat, Inc.
+ */
+
+#ifndef __NMS_KEYFILE_STORAGE_H__
+#define __NMS_KEYFILE_STORAGE_H__
+
+#include "c-list/src/c-list.h"
+#include "settings/nm-settings-storage.h"
+#include "nms-keyfile-utils.h"
+
+/*****************************************************************************/
+
+#define NMS_TYPE_KEYFILE_STORAGE (nms_keyfile_storage_get_type ())
+#define NMS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorage))
+#define NMS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass))
+#define NMS_IS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_STORAGE))
+#define NMS_IS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_STORAGE))
+#define NMS_KEYFILE_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass))
+
+typedef struct {
+ NMSettingsStorage parent;
+
+ 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.
+ *
+ * After reload-all, this list is cleared again. */
+ struct _NMSKeyfileConnReloadHead *_reload_data_head;
+
+ NMSKeyfileStorageType storage_type_exported;
+
+ bool is_nm_generated:1;
+ bool is_volatile:1;
+} NMSKeyfileStorage;
+
+typedef struct _NMSKeyfileStorageClass NMSKeyfileStorageClass;
+
+GType nms_keyfile_storage_get_type (void);
+
+struct _NMSKeyfilePlugin;
+
+NMSKeyfileStorage *nms_keyfile_storage_new (struct _NMSKeyfilePlugin *plugin,
+ const char *uuid);
+
+void _nms_keyfile_storage_clear (NMSKeyfileStorage *storage);
+
+/*****************************************************************************/
+
+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));
+}
+
+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));
+}
+
+/*****************************************************************************/
+
+static inline gboolean
+nm_settings_storage_is_keyfile (NMSettingsStorage *self,
+ gboolean *out_is_in_memory)
+{
+ 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;
+}
+
+static inline gboolean
+nm_settings_storage_is_in_memory (NMSettingsStorage *self)
+{
+ return NMS_IS_KEYFILE_STORAGE (self)
+ && (((NMSKeyfileStorage *) self)->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN);
+}
+
+enum _NMSettingsConnectionIntFlags;
+
+void nm_settings_storage_load_sett_flags (NMSettingsStorage *self,
+ enum _NMSettingsConnectionIntFlags *sett_flags,
+ enum _NMSettingsConnectionIntFlags *sett_mask);
+
+#endif /* __NMS_KEYFILE_STORAGE_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index f2f9f37b7c..af0dcc56d1 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -14,7 +14,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * (C) Copyright 2010 Red Hat, Inc.
+ * (C) Copyright 2010 - 2018 Red Hat, Inc.
*/
#include "nm-default.h"
@@ -41,7 +41,8 @@ nms_keyfile_loaded_uuid_filename (const char *dirname,
char filename[250];
nm_assert (dirname && dirname[0] == '/');
- nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/'));
+ nm_assert ( nm_utils_is_uuid (uuid)
+ && !strchr (uuid, '/'));
if (g_snprintf (filename,
sizeof (filename),
@@ -167,7 +168,8 @@ nms_keyfile_loaded_uuid_write (const char *dirname,
gs_free char *full_filename = NULL;
nm_assert (dirname && dirname[0] == '/');
- nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/'));
+ nm_assert ( nm_utils_is_uuid (uuid)
+ && !strchr (uuid, '/'));
nm_assert (!loaded_path || loaded_path[0] == '/');
full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE);
@@ -294,22 +296,3 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype,
NM_SET_OUT (out_st, st);
return TRUE;
}
-
-/*****************************************************************************/
-
-const char *
-nms_keyfile_utils_get_path (void)
-{
- static char *path = NULL;
-
- if (G_UNLIKELY (!path)) {
- path = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
- NM_CONFIG_KEYFILE_GROUP_KEYFILE,
- NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
- NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
- if (!path)
- path = g_strdup (""NM_KEYFILE_PATH_NAME_ETC_DEFAULT"");
- }
- return path;
-}
-
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index 30b033adba..59b86d3142 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -14,7 +14,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * (C) Copyright 2010-2016 Red Hat, Inc.
+ * (C) Copyright 2010 - 2018 Red Hat, Inc.
*/
#ifndef __NMS_KEYFILE_UTILS_H__
@@ -22,18 +22,18 @@
#include "NetworkManagerUtils.h"
-#define NMS_KEYFILE_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory")
-#define NMS_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")"
-#define NMS_KEYFILE_CONNECTION_LOG_ARG(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con))
-#define NMS_KEYFILE_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)"
-#define NMS_KEYFILE_CONNECTION_LOG_ARGD(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con)
-
typedef enum {
NMS_KEYFILE_FILETYPE_KEYFILE,
NMS_KEYFILE_FILETYPE_NMLOADED,
} NMSKeyfileFiletype;
-const char *nms_keyfile_utils_get_path (void);
+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 */
+} NMSKeyfileStorageType;
/*****************************************************************************/
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index a0c3c17b98..54e62b7373 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -168,6 +168,8 @@ _handler_write (NMConnection *connection,
static gboolean
_internal_write_connection (NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
const char *keyfile_dir,
const char *profile_dir,
gboolean with_extension,
@@ -212,6 +214,21 @@ _internal_write_connection (NMConnection *connection,
kf_file = nm_keyfile_write (connection, _handler_write, &info, error);
if (!kf_file)
return FALSE;
+
+ if (is_nm_generated) {
+ g_key_file_set_boolean (kf_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_NM_GENERATED,
+ TRUE);
+ }
+
+ if (is_volatile) {
+ g_key_file_set_boolean (kf_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_VOLATILE,
+ TRUE);
+ }
+
kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error);
if (!kf_content_buf)
return FALSE;
@@ -337,8 +354,12 @@ _internal_write_connection (NMConnection *connection,
gboolean
nms_keyfile_writer_connection (NMConnection *connection,
- gboolean save_to_disk,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ const char *keyfile_dir,
+ const char *profile_dir,
const char *existing_path,
+ gboolean existing_path_read_only,
gboolean force_rename,
NMSKeyfileWriterAllowFilenameCb allow_filename_cb,
gpointer allow_filename_user_data,
@@ -347,21 +368,16 @@ nms_keyfile_writer_connection (NMConnection *connection,
gboolean *out_reread_same,
GError **error)
{
- const char *keyfile_dir;
-
- if (save_to_disk)
- keyfile_dir = nms_keyfile_utils_get_path ();
- else
- keyfile_dir = NM_KEYFILE_PATH_NAME_RUN;
-
return _internal_write_connection (connection,
+ is_nm_generated,
+ is_volatile,
keyfile_dir,
- nms_keyfile_utils_get_path (),
+ profile_dir,
TRUE,
0,
0,
existing_path,
- FALSE,
+ existing_path_read_only,
force_rename,
allow_filename_cb,
allow_filename_user_data,
@@ -382,6 +398,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
GError **error)
{
return _internal_write_connection (connection,
+ FALSE,
+ FALSE,
keyfile_dir,
keyfile_dir,
FALSE,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index db41b81c50..4fb9a20638 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -27,8 +27,12 @@ typedef gboolean (*NMSKeyfileWriterAllowFilenameCb) (const char *check_filename,
gpointer allow_filename_user_data);
gboolean nms_keyfile_writer_connection (NMConnection *connection,
- gboolean save_to_disk,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ const char *keyfile_dir,
+ const char *profile_dir,
const char *existing_path,
+ gboolean existing_path_read_only,
gboolean force_rename,
NMSKeyfileWriterAllowFilenameCb allow_filename_cb,
gpointer allow_filename_user_data,
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
index 232c690431..9b035c2034 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -72,6 +72,9 @@ check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int
\
_connection = nms_keyfile_reader_from_file (full_filename, \
NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
(nmtst_get_rand_uint32 () % 2) ? &_error : NULL); \
nmtst_assert_success (_connection, _error); \
nmtst_assert_connection_verifies_without_normalization (_connection); \
diff --git a/tools/meson-post-install.sh b/tools/meson-post-install.sh
index d2474f475d..76b44371b1 100755
--- a/tools/meson-post-install.sh
+++ b/tools/meson-post-install.sh
@@ -30,6 +30,7 @@ for dir in "${pkgconfdir}/conf.d" \
"${pkglibdir}/dispatcher.d/no-wait.d" \
"${pkglibdir}/dispatcher.d/pre-down.d" \
"${pkglibdir}/dispatcher.d/pre-up.d" \
+ "${pkglibdir}/system-connections" \
"${pkglibdir}/VPN"; do
mkdir -p "${DESTDIR}${dir}"
chmod 0755 "${DESTDIR}${dir}"