about summary refs log tree commit diff
path: root/libnm-util/nm-setting.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-util/nm-setting.c')
-rw-r--r--libnm-util/nm-setting.c568
1 files changed, 410 insertions, 158 deletions
diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c
index a3186284..0e8a06c4 100644
--- a/libnm-util/nm-setting.c
+++ b/libnm-util/nm-setting.c
@@ -29,6 +29,7 @@
 #include "nm-setting-private.h"
 #include "nm-setting-connection.h"
 #include "nm-utils.h"
+#include "nm-utils-private.h"
 
 /**
  * SECTION:nm-setting
@@ -37,7 +38,7 @@
  *
  * Each #NMSetting contains properties that describe configuration that applies
  * to a specific network layer (like IPv4 or IPv6 configuration) or device type
- * (like Ethernet, or WiFi).  A collection of individual settings together
+ * (like Ethernet, or Wi-Fi).  A collection of individual settings together
  * make up an #NMConnection. Each property is strongly typed and usually has
  * a number of allowed values.  See each #NMSetting subclass for a description
  * of properties and allowed values.
@@ -65,7 +66,14 @@ G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
 #define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
 
 typedef struct {
-	char *name;
+	const char *name;
+	GType type;
+	guint32 priority;
+	GQuark error_quark;
+} SettingInfo;
+
+typedef struct {
+	const SettingInfo *info;
 } NMSettingPrivate;
 
 enum {
@@ -75,6 +83,197 @@ enum {
 	PROP_LAST
 };
 
+/*************************************************************/
+
+static GHashTable *registered_settings = NULL;
+static GHashTable *registered_settings_by_type = NULL;
+
+static gboolean
+_nm_gtype_equal (gconstpointer v1, gconstpointer v2)
+{
+	return *((const GType *) v1) == *((const GType *) v2);
+}
+static guint
+_nm_gtype_hash (gconstpointer v)
+{
+	return *((const GType *) v);
+}
+
+static void __attribute__((constructor))
+_ensure_registered (void)
+{
+	if (G_UNLIKELY (registered_settings == NULL)) {
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+		g_type_init ();
+#endif
+		_nm_value_transforms_register ();
+		registered_settings = g_hash_table_new (g_str_hash, g_str_equal);
+		registered_settings_by_type = g_hash_table_new (_nm_gtype_hash, _nm_gtype_equal);
+	}
+}
+
+#define _ensure_setting_info(self, priv) \
+	G_STMT_START { \
+		NMSettingPrivate *_priv_esi = (priv); \
+		if (G_UNLIKELY (!_priv_esi->info)) { \
+			_priv_esi->info = _nm_setting_lookup_setting_by_type (G_OBJECT_TYPE (self)); \
+			g_assert (_priv_esi->info); \
+		} \
+	} G_STMT_END
+
+/*************************************************************/
+
+/*
+ * _nm_register_setting:
+ * @name: the name of the #NMSetting object to register
+ * @type: the #GType of the #NMSetting
+ * @priority: the sort priority of the setting, see below
+ * @error_quark: the setting's error quark
+ *
+ * INTERNAL ONLY: registers a setting's internal properties, like its priority
+ * and its error quark type, with libnm-util.
+ *
+ * A setting's priority should roughly follow the OSI layer model, but it also
+ * controls which settings get asked for secrets first.  Thus settings which
+ * relate to things that must be working first, like hardware, should get a
+ * higher priority than things which layer on top of the hardware.  For example,
+ * the GSM/CDMA settings should provide secrets before the PPP setting does,
+ * because a PIN is required to unlock the device before PPP can even start.
+ * Even settings without secrets should be assigned the right priority.
+ *
+ * 0: reserved for the Connection setting
+ *
+ * 1: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc.
+ * These priority 1 settings are also "base types", which means that at least
+ * one of them is required for the connection to be valid, and their name is
+ * valid in the 'type' property of the Connection setting.
+ *
+ * 2: hardware-related auxiliary settings that require a base setting to be
+ * successful first, like Wi-Fi security, 802.1x, etc.
+ *
+ * 3: hardware-independent settings that are required before IP connectivity
+ * can be established, like PPP, PPPoE, etc.
+ *
+ * 4: IP-level stuff
+ */
+void
+(_nm_register_setting) (const char *name,
+                        const GType type,
+                        const guint32 priority,
+                        const GQuark error_quark)
+{
+	SettingInfo *info;
+
+	g_return_if_fail (name != NULL && *name);
+	g_return_if_fail (type != G_TYPE_INVALID);
+	g_return_if_fail (type != G_TYPE_NONE);
+	g_return_if_fail (error_quark != 0);
+	g_return_if_fail (priority <= 4);
+
+	_ensure_registered ();
+
+	if (G_LIKELY ((info = g_hash_table_lookup (registered_settings, name)))) {
+		g_return_if_fail (info->type == type);
+		g_return_if_fail (info->error_quark == error_quark);
+		g_return_if_fail (info->priority == priority);
+		g_return_if_fail (g_strcmp0 (info->name, name) == 0);
+		return;
+	}
+	g_return_if_fail (g_hash_table_lookup (registered_settings_by_type, &type) == NULL);
+
+	if (priority == 0)
+		g_assert_cmpstr (name, ==, NM_SETTING_CONNECTION_SETTING_NAME);
+
+	info = g_slice_new0 (SettingInfo);
+	info->type = type;
+	info->priority = priority;
+	info->error_quark = error_quark;
+	info->name = name;
+	g_hash_table_insert (registered_settings, (void *) info->name, info);
+	g_hash_table_insert (registered_settings_by_type, &info->type, info);
+}
+
+static const SettingInfo *
+_nm_setting_lookup_setting_by_type (GType type)
+{
+	_ensure_registered ();
+	return g_hash_table_lookup (registered_settings_by_type, &type);
+}
+
+static guint32
+_get_setting_type_priority (GType type)
+{
+	const SettingInfo *info;
+
+	g_return_val_if_fail (g_type_is_a (type, NM_TYPE_SETTING), G_MAXUINT32);
+
+	info = _nm_setting_lookup_setting_by_type (type);
+	return info->priority;
+}
+
+gboolean
+_nm_setting_type_is_base_type (GType type)
+{
+	/* Historical oddity: PPPoE is a base-type even though it's not
+	 * priority 1.  It needs to be sorted *after* lower-level stuff like
+	 * Wi-Fi security or 802.1x for secrets, but it's still allowed as a
+	 * base type.
+	 */
+	return _get_setting_type_priority (type) == 1 || (type == NM_TYPE_SETTING_PPPOE);
+}
+
+gboolean
+_nm_setting_is_base_type (NMSetting *setting)
+{
+	return _nm_setting_type_is_base_type (G_OBJECT_TYPE (setting));
+}
+
+GType
+_nm_setting_lookup_setting_type (const char *name)
+{
+	SettingInfo *info;
+
+	g_return_val_if_fail (name != NULL, G_TYPE_NONE);
+
+	_ensure_registered ();
+
+	info = g_hash_table_lookup (registered_settings, name);
+	return info ? info->type : G_TYPE_INVALID;
+}
+
+GType
+_nm_setting_lookup_setting_type_by_quark (GQuark error_quark)
+{
+	SettingInfo *info;
+	GHashTableIter iter;
+
+	_ensure_registered ();
+
+	g_hash_table_iter_init (&iter, registered_settings);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &info)) {
+		if (info->error_quark == error_quark)
+			return info->type;
+	}
+	return G_TYPE_INVALID;
+}
+
+gint
+_nm_setting_compare_priority (gconstpointer a, gconstpointer b)
+{
+	guint32 prio_a, prio_b;
+
+	prio_a = _get_setting_type_priority (G_OBJECT_TYPE (a));
+	prio_b = _get_setting_type_priority (G_OBJECT_TYPE (b));
+
+	if (prio_a < prio_b)
+		return -1;
+	else if (prio_a == prio_b)
+		return 0;
+	return 1;
+}
+
+/*************************************************************/
+
 static void
 destroy_gvalue (gpointer data)
 {
@@ -104,7 +303,6 @@ nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags)
 	guint n_property_specs;
 	guint i;
 
-	g_return_val_if_fail (setting != NULL, NULL);
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 
 	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
@@ -121,7 +319,8 @@ nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags)
 		GParamSpec *prop_spec = property_specs[i];
 		GValue *value;
 
-		if (!(prop_spec->flags & NM_SETTING_PARAM_SERIALIZE))
+		/* 'name' doesn't get serialized */
+		if (strcmp (g_param_spec_get_name (prop_spec), NM_SETTING_NAME) == 0)
 			continue;
 
 		if (   (flags & NM_SETTING_HASH_FLAG_NO_SECRETS)
@@ -144,8 +343,8 @@ nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags)
 	}
 	g_free (property_specs);
 
-	/* Don't return empty hashes */
-	if (g_hash_table_size (hash) < 1) {
+	/* Don't return empty hashes, except for base types */
+	if (g_hash_table_size (hash) < 1 && !_nm_setting_is_base_type (setting)) {
 		g_hash_table_destroy (hash);
 		hash = NULL;
 	}
@@ -153,39 +352,6 @@ nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags)
 	return hash;
 }
 
-typedef struct {
-	GObjectClass *class;
-	guint n_params;
-	GParameter *params;
-} NMSettingFromHashInfo;
-
-static void
-one_property_cb (gpointer key, gpointer val, gpointer user_data)
-{
-	const char *prop_name = (char *) key;
-	GValue *src_value = (GValue *) val;
-	NMSettingFromHashInfo *info = (NMSettingFromHashInfo *) user_data;
-	GValue *dst_value = &info->params[info->n_params].value;
-	GParamSpec *param_spec;
-
-	param_spec = g_object_class_find_property (info->class, prop_name);
-	if (!param_spec || !(param_spec->flags & NM_SETTING_PARAM_SERIALIZE)) {
-		/* Oh, we're so nice and only warn, maybe it should be a fatal error? */
-		g_warning ("Ignoring invalid property '%s'", prop_name);
-		return;
-	}
-
-	g_value_init (dst_value, G_VALUE_TYPE (src_value));
-	if (g_value_transform (src_value, dst_value)) {
-		info->params[info->n_params].name = prop_name;
-		info->n_params++;
-	} else {
-		g_warning ("Ignoring property '%s' with invalid type (%s)",
-		           prop_name, G_VALUE_TYPE_NAME (src_value));
-		g_value_unset (dst_value);
-	}
-}
-
 /**
  * nm_setting_new_from_hash:
  * @setting_type: the #NMSetting type which the hash contains properties for
@@ -205,28 +371,53 @@ one_property_cb (gpointer key, gpointer val, gpointer user_data)
 NMSetting *
 nm_setting_new_from_hash (GType setting_type, GHashTable *hash)
 {
+	GHashTableIter iter;
 	NMSetting *setting;
-	NMSettingFromHashInfo info;
+	const char *prop_name;
+	GValue *src_value;
+	GObjectClass *class;
+	guint n_params = 0;
+	GParameter *params;
 	int i;
 
 	g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
 	g_return_val_if_fail (hash != NULL, NULL);
 
-	info.class = g_type_class_ref (setting_type);
-	info.n_params = 0;
-	info.params = g_new0 (GParameter, g_hash_table_size (hash));
+	/* g_type_class_ref() ensures the setting class is created if it hasn't
+	 * already been used.
+	 */
+	class = g_type_class_ref (setting_type);
+	params = g_new0 (GParameter, g_hash_table_size (hash));
+
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter, (gpointer) &prop_name, (gpointer) &src_value)) {
+		GValue *dst_value = &params[n_params].value;
+		GParamSpec *param_spec;
+
+		param_spec = g_object_class_find_property (class, prop_name);
+		if (!param_spec) {
+			/* Oh, we're so nice and only warn, maybe it should be a fatal error? */
+			g_warning ("Ignoring invalid property '%s'", prop_name);
+			continue;
+		}
 
-	g_hash_table_foreach (hash, one_property_cb, &info);
+		g_value_init (dst_value, G_VALUE_TYPE (src_value));
+		if (g_value_transform (src_value, dst_value))
+			params[n_params++].name = prop_name;
+		else {
+			g_warning ("Ignoring property '%s' with invalid type (%s)",
+				       prop_name, G_VALUE_TYPE_NAME (src_value));
+			g_value_unset (dst_value);
+		}
+	}
 
-	setting = (NMSetting *) g_object_newv (setting_type, info.n_params, info.params);
+	setting = (NMSetting *) g_object_newv (setting_type, n_params, params);
 
-	for (i = 0; i < info.n_params; i++) {
-		GValue *v = &info.params[i].value;
-		g_value_unset (v);
-	}
+	for (i = 0; i < n_params; i++)
+		g_value_unset (&params[i].value);
 
-	g_free (info.params);
-	g_type_class_unref (info.class);
+	g_free (params);
+	g_type_class_unref (class);
 
 	return setting;
 }
@@ -238,7 +429,7 @@ duplicate_setting (NMSetting *setting,
                    GParamFlags flags,
                    gpointer user_data)
 {
-	if (flags & G_PARAM_WRITABLE)
+	if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE)
 		g_object_set_property (G_OBJECT (user_data), name, value);
 }
 
@@ -267,6 +458,28 @@ nm_setting_duplicate (NMSetting *setting)
 	return NM_SETTING (dup);
 }
 
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+NMSetting *
+nm_setting_find_in_list (GSList     *settings_list,
+                         const char *setting_name)
+{
+	GSList *found;
+
+	found = g_slist_find_custom (settings_list, setting_name, find_setting_by_name);
+	if (found)
+		return found->data;
+	else
+		return NULL;
+}
+
 /**
  * nm_setting_get_name:
  * @setting: the #NMSetting
@@ -279,9 +492,12 @@ nm_setting_duplicate (NMSetting *setting)
 const char *
 nm_setting_get_name (NMSetting *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+	NMSettingPrivate *priv;
 
-	return NM_SETTING_GET_PRIVATE (setting)->name;
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+	priv = NM_SETTING_GET_PRIVATE (setting);
+	_ensure_setting_info (setting, priv);
+	return priv->info->name;
 }
 
 /**
@@ -302,8 +518,7 @@ gboolean
 nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error)
 {
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
-	if (error)
-		g_return_val_if_fail (*error == NULL, FALSE);
+	g_return_val_if_fail (!error || *error == NULL, FALSE);
 
 	if (NM_SETTING_GET_CLASS (setting)->verify)
 		return NM_SETTING_GET_CLASS (setting)->verify (setting, all_settings, error);
@@ -317,8 +532,8 @@ compare_property (NMSetting *setting,
 	              const GParamSpec *prop_spec,
 	              NMSettingCompareFlags flags)
 {
-	GValue value1 = { 0 };
-	GValue value2 = { 0 };
+	GValue value1 = G_VALUE_INIT;
+	GValue value2 = G_VALUE_INIT;
 	gboolean different;
 
 	/* Handle compare flags */
@@ -398,6 +613,9 @@ nm_setting_compare (NMSetting *a,
 			&& (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
 			continue;
 
+		if ((flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_spec->flags & NM_SETTING_PARAM_INFERRABLE))
+			continue;
+
 		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
 		    && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
 			continue;
@@ -420,6 +638,9 @@ should_compare_prop (NMSetting *setting,
 	    && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
 		return FALSE;
 
+	if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
+		return FALSE;
+
 	if (prop_flags & NM_SETTING_PARAM_SECRET) {
 		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
 
@@ -481,7 +702,6 @@ nm_setting_diff (NMSetting *a,
 	gboolean results_created = FALSE;
 
 	g_return_val_if_fail (results != NULL, FALSE);
-	g_return_val_if_fail (a != NULL, FALSE);
 	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
 	if (b) {
 		g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
@@ -512,7 +732,6 @@ nm_setting_diff (NMSetting *a,
 
 	for (i = 0; i < n_property_specs; i++) {
 		GParamSpec *prop_spec = property_specs[i];
-		GValue a_value = { 0 }, b_value = { 0 };
 		NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN, tmp;
 		gboolean different = TRUE;
 
@@ -523,22 +742,22 @@ nm_setting_diff (NMSetting *a,
 			continue;
 
 		if (b) {
-			g_value_init (&a_value, prop_spec->value_type);
-			g_object_get_property (G_OBJECT (a), prop_spec->name, &a_value);
-
-			g_value_init (&b_value, prop_spec->value_type);
-			g_object_get_property (G_OBJECT (b), prop_spec->name, &b_value);
-
-			different = !!g_param_values_cmp (prop_spec, &a_value, &b_value);
+			different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
 			if (different) {
-				if (!g_param_value_defaults (prop_spec, &a_value))
+				GValue value = G_VALUE_INIT;
+
+				g_value_init (&value, prop_spec->value_type);
+				g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
+				if (!g_param_value_defaults (prop_spec, &value))
 					r |= a_result;
-				if (!g_param_value_defaults (prop_spec, &b_value))
+
+				g_value_reset (&value);
+				g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
+				if (!g_param_value_defaults (prop_spec, &value))
 					r |= b_result;
-			}
 
-			g_value_unset (&a_value);
-			g_value_unset (&b_value);
+				g_value_unset (&value);
+			}
 		} else
 			r = a_result;  /* only in A */
 
@@ -582,7 +801,7 @@ nm_setting_enumerate_values (NMSetting *setting,
 	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
 	for (i = 0; i < n_property_specs; i++) {
 		GParamSpec *prop_spec = property_specs[i];
-		GValue value = { 0 };
+		GValue value = G_VALUE_INIT;
 
 		g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec));
 		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
@@ -604,46 +823,68 @@ nm_setting_enumerate_values (NMSetting *setting,
 void
 nm_setting_clear_secrets (NMSetting *setting)
 {
+	_nm_setting_clear_secrets (setting);
+}
+
+gboolean
+_nm_setting_clear_secrets (NMSetting *setting)
+{
 	GParamSpec **property_specs;
 	guint n_property_specs;
 	guint i;
+	gboolean changed = FALSE;
 
-	g_return_if_fail (NM_IS_SETTING (setting));
+	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 
 	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
 
 	for (i = 0; i < n_property_specs; i++) {
 		GParamSpec *prop_spec = property_specs[i];
-		GValue value = { 0 };
 
 		if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
+			GValue value = G_VALUE_INIT;
+
 			g_value_init (&value, prop_spec->value_type);
-			g_param_value_set_default (prop_spec, &value);
-			g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
+			g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+			if (!g_param_value_defaults (prop_spec, &value)) {
+				g_param_value_set_default (prop_spec, &value);
+				g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
+				changed = TRUE;
+			}
 			g_value_unset (&value);
 		}
 	}
 
 	g_free (property_specs);
+
+	return changed;
 }
 
-static void
+static gboolean
 clear_secrets_with_flags (NMSetting *setting,
 	                      GParamSpec *pspec,
 	                      NMSettingClearSecretsWithFlagsFn func,
 	                      gpointer user_data)
 {
-	GValue value = { 0 };
 	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	gboolean changed = FALSE;
 
 	/* Clear the secret if the user function says to do so */
 	nm_setting_get_secret_flags (setting, pspec->name, &flags, NULL);
 	if (func (setting, pspec->name, flags, user_data) == TRUE) {
+		GValue value = G_VALUE_INIT;
+
 		g_value_init (&value, pspec->value_type);
-		g_param_value_set_default (pspec, &value);
-		g_object_set_property (G_OBJECT (setting), pspec->name, &value);
+		g_object_get_property (G_OBJECT (setting), pspec->name, &value);
+		if (!g_param_value_defaults (pspec, &value)) {
+			g_param_value_set_default (pspec, &value);
+			g_object_set_property (G_OBJECT (setting), pspec->name, &value);
+			changed = TRUE;
+		}
 		g_value_unset (&value);
 	}
+
+	return changed;
 }
 
 /**
@@ -660,25 +901,35 @@ nm_setting_clear_secrets_with_flags (NMSetting *setting,
                                      NMSettingClearSecretsWithFlagsFn func,
                                      gpointer user_data)
 {
+	_nm_setting_clear_secrets_with_flags (setting, func, user_data);
+}
+
+gboolean
+_nm_setting_clear_secrets_with_flags (NMSetting *setting,
+                                      NMSettingClearSecretsWithFlagsFn func,
+                                      gpointer user_data)
+{
 	GParamSpec **property_specs;
 	guint n_property_specs;
 	guint i;
+	gboolean changed = FALSE;
 
-	g_return_if_fail (setting);
-	g_return_if_fail (NM_IS_SETTING (setting));
-	g_return_if_fail (func != NULL);
+	g_return_val_if_fail (setting, FALSE);
+	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+	g_return_val_if_fail (func != NULL, FALSE);
 
 	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
 	for (i = 0; i < n_property_specs; i++) {
 		if (property_specs[i]->flags & NM_SETTING_PARAM_SECRET) {
-			NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting,
-			                                                          property_specs[i],
-			                                                          func,
-			                                                          user_data);
+			changed |= NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting,
+			                                                                     property_specs[i],
+			                                                                     func,
+			                                                                     user_data);
 		}
 	}
 
 	g_free (property_specs);
+	return changed;
 }
 
 /**
@@ -708,12 +959,11 @@ nm_setting_need_secrets (NMSetting *setting)
 	return secrets;
 }
 
-static gboolean
+static int
 update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
 {
 	GParamSpec *prop_spec;
-	GValue transformed_value = { 0 };
-	gboolean success = FALSE;
+	GValue transformed_value = G_VALUE_INIT;
 
 	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
 	if (!prop_spec) {
@@ -721,27 +971,40 @@ update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **
 		             NM_SETTING_ERROR,
 		             NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
 		             "%s", key);
-		return FALSE;
+		return NM_SETTING_UPDATE_SECRET_ERROR;
 	}
 
 	/* Silently ignore non-secrets */
 	if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET))
-		return TRUE;
+		return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
 
 	if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) {
+		if (G_VALUE_HOLDS_STRING (value) && G_IS_PARAM_SPEC_STRING (prop_spec)) {
+			/* String is expected to be a common case. Handle it specially and check whether
+			 * the value is already set. Otherwise, we just reset the property and
+			 * assume the value got modified. */
+			char *v;
+
+			g_object_get (G_OBJECT (setting), prop_spec->name, &v, NULL);
+			if (g_strcmp0 (v, g_value_get_string (value)) == 0) {
+				g_free (v);
+				return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+			}
+			g_free (v);
+		}
 		g_object_set_property (G_OBJECT (setting), prop_spec->name, value);
-		success = TRUE;
-	} else if (g_value_transform (value, &transformed_value)) {
+		return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+	}
+	if (g_value_transform (value, &transformed_value)) {
 		g_object_set_property (G_OBJECT (setting), prop_spec->name, &transformed_value);
 		g_value_unset (&transformed_value);
-		success = TRUE;
-	} else {
-		g_set_error (error,
-		             NM_SETTING_ERROR,
-		             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
-		             "%s", key);
+		return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
 	}
-	return success;
+	g_set_error (error,
+	             NM_SETTING_ERROR,
+	             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+	             "%s", key);
+	return NM_SETTING_UPDATE_SECRET_ERROR;
 }
 
 /**
@@ -760,29 +1023,41 @@ update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **
 gboolean
 nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error)
 {
+	return _nm_setting_update_secrets (setting, secrets, error) != NM_SETTING_UPDATE_SECRET_ERROR;
+}
+
+NMSettingUpdateSecretResult
+_nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error)
+{
 	GHashTableIter iter;
 	gpointer key, data;
 	GError *tmp_error = NULL;
+	NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
 
-	g_return_val_if_fail (setting != NULL, FALSE);
-	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
-	g_return_val_if_fail (secrets != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_UPDATE_SECRET_ERROR);
+	g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
 	if (error)
-		g_return_val_if_fail (*error == NULL, FALSE);
+		g_return_val_if_fail (*error == NULL, NM_SETTING_UPDATE_SECRET_ERROR);
 
 	g_hash_table_iter_init (&iter, secrets);
 	while (g_hash_table_iter_next (&iter, &key, &data)) {
+		int success;
 		const char *secret_key = (const char *) key;
 		GValue *secret_value = (GValue *) data;
 
-		NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
-		if (tmp_error) {
+		success = NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
+		g_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
+
+		if (success == NM_SETTING_UPDATE_SECRET_ERROR) {
 			g_propagate_error (error, tmp_error);
-			return FALSE;
+			return NM_SETTING_UPDATE_SECRET_ERROR;
 		}
+
+		if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
+			result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
 	}
 
-	return TRUE;
+	return result;
 }
 
 static gboolean
@@ -851,7 +1126,6 @@ nm_setting_get_secret_flags (NMSetting *setting,
                              NMSettingSecretFlags *out_flags,
                              GError **error)
 {
-	g_return_val_if_fail (setting != NULL, FALSE);
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 	g_return_val_if_fail (secret_name != NULL, FALSE);
 
@@ -883,7 +1157,7 @@ set_secret_flags (NMSetting *setting,
  * @flags: the #NMSettingSecretFlags for the secret
  * @error: location to store error, or %NULL
  *
- * For a given secret, retrieves the #NMSettingSecretFlags describing how to
+ * For a given secret, stores the #NMSettingSecretFlags describing how to
  * handle that secret.
  *
  * Returns: %TRUE on success (if the given secret name was a valid property of
@@ -895,7 +1169,6 @@ nm_setting_set_secret_flags (NMSetting *setting,
                              NMSettingSecretFlags flags,
                              GError **error)
 {
-	g_return_val_if_fail (setting != NULL, FALSE);
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 	g_return_val_if_fail (secret_name != NULL, FALSE);
 	g_return_val_if_fail (flags <= NM_SETTING_SECRET_FLAGS_ALL, FALSE);
@@ -934,11 +1207,13 @@ nm_setting_to_string (NMSetting *setting)
 
 	for (i = 0; i < n_property_specs; i++) {
 		GParamSpec *prop_spec = property_specs[i];
-		GValue value = { 0 };
+		GValue value = G_VALUE_INIT;
 		char *value_str;
-		gboolean is_serializable;
 		gboolean is_default;
 
+		if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
+			continue;
+
 		g_value_init (&value, prop_spec->value_type);
 		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
 
@@ -946,22 +1221,14 @@ nm_setting_to_string (NMSetting *setting)
 		g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str);
 		g_free (value_str);
 
-		is_serializable = prop_spec->flags & NM_SETTING_PARAM_SERIALIZE;
 		is_default = g_param_value_defaults (prop_spec, &value);
-
 		g_value_unset (&value);
 
-		if (is_serializable || is_default) {
-			g_string_append (string, " (");
-
-			if (is_serializable)
-				g_string_append_c (string, 's');
-			if (is_default)
-				g_string_append_c (string, 'd');
-
-			g_string_append_c (string, ')');
-		}
-
+		g_string_append (string, " (");
+		g_string_append_c (string, 's');
+		if (is_default)
+			g_string_append_c (string, 'd');
+		g_string_append_c (string, ')');
 		g_string_append_c (string, '\n');
 	}
 
@@ -1001,48 +1268,34 @@ nm_setting_init (NMSetting *setting)
 
 static GObject*
 constructor (GType type,
-		   guint n_construct_params,
-		   GObjectConstructParam *construct_params)
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
 {
 	GObject *object;
-	NMSettingPrivate *priv;
 
 	object = G_OBJECT_CLASS (nm_setting_parent_class)->constructor (type,
-													    n_construct_params,
-													    construct_params);
-	if (!object)
-		return NULL;
-
-	priv = NM_SETTING_GET_PRIVATE (object);
-	if (!priv->name) {
-		g_warning ("Setting name is not set.");
-		g_object_unref (object);
-		object = NULL;
-	}
+	                                                                n_construct_params,
+	                                                                construct_params);
 
+	_ensure_setting_info (object, NM_SETTING_GET_PRIVATE (object));
 	return object;
 }
 
 static void
-finalize (GObject *object)
-{
-	NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
-
-	g_free (priv->name);
-
-	G_OBJECT_CLASS (nm_setting_parent_class)->finalize (object);
-}
-
-static void
 set_property (GObject *object, guint prop_id,
-		    const GValue *value, GParamSpec *pspec)
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_NAME:
-		g_free (priv->name);
-		priv->name = g_value_dup_string (value);
+		/* The setter for NAME is deprecated and should not be used anymore.
+		 * Keep the setter for NAME to remain backward compatible.
+		 * Only assert that the caller does not try to set the name to a different value
+		 * then the registered name, which would be extra wrong.
+		 **/
+		_ensure_setting_info (object, priv);
+		g_return_if_fail (!g_strcmp0 (priv->info->name, g_value_get_string (value)));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1052,7 +1305,7 @@ set_property (GObject *object, guint prop_id,
 
 static void
 get_property (GObject *object, guint prop_id,
-		    GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMSetting *setting = NM_SETTING (object);
 
@@ -1077,7 +1330,6 @@ nm_setting_class_init (NMSettingClass *setting_class)
 	object_class->constructor  = constructor;
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize     = finalize;
 
 	setting_class->update_one_secret = update_one_secret;
 	setting_class->get_secret_flags = get_secret_flags;
@@ -1092,7 +1344,7 @@ nm_setting_class_init (NMSettingClass *setting_class)
 	 *
 	 * The setting's name, which uniquely identifies the setting within the
 	 * connection.  Each setting type has a name unique to that type, for
-	 * example 'ppp' or 'wireless' or 'wired'.
+	 * example "ppp" or "wireless" or "wired".
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_NAME,