diff options
-rw-r--r-- | libnm-core/nm-core-internal.h | 52 | ||||
-rw-r--r-- | libnm-core/nm-keyfile.c | 147 | ||||
-rw-r--r-- | libnm-core/nm-setting-private.h | 11 | ||||
-rw-r--r-- | libnm-core/nm-setting.c | 586 | ||||
-rw-r--r-- | libnm-core/nm-setting.h | 8 |
5 files changed, 708 insertions, 96 deletions
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 21c45ab425..7338cd27af 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -173,6 +173,34 @@ NMSettingPriority _nm_setting_get_setting_priority (NMSetting *setting); gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value); +/*****************************************************************************/ + +GHashTable *_nm_setting_gendata_hash (NMSetting *setting, + gboolean create_if_necessary); + +void _nm_setting_gendata_notify (NMSetting *setting, + gboolean keys_changed); + +guint _nm_setting_gendata_get_all (NMSetting *setting, + const char *const**out_names, + GVariant *const**out_values); + +gboolean _nm_setting_gendata_reset_from_hash (NMSetting *setting, + GHashTable *new); + +void _nm_setting_gendata_to_gvalue (NMSetting *setting, + GValue *value); + +GVariant *nm_setting_gendata_get (NMSetting *setting, + const char *name); + +const char *const*nm_setting_gendata_get_all_names (NMSetting *setting, + guint *out_len); + +GVariant *const*nm_setting_gendata_get_all_values (NMSetting *setting); + +/*****************************************************************************/ + #define NM_UTILS_HWADDR_LEN_MAX_STR (NM_UTILS_HWADDR_LEN_MAX * 3) guint8 *_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length); @@ -481,6 +509,8 @@ gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting); /*****************************************************************************/ +typedef struct _NMSettInfoSetting NMSettInfoSetting; + typedef GVariant *(*NMSettingPropertyGetFunc) (NMSetting *setting, const char *property); typedef GVariant *(*NMSettingPropertySynthFunc) (NMSetting *setting, @@ -516,14 +546,32 @@ typedef struct { } NMSettInfoProperty; typedef struct { -} NMSettInfoSettDetail; + const GVariantType *(*get_variant_type) (const struct _NMSettInfoSetting *sett_info, + const char *name, + GError **error); +} NMSettInfoSettGendata; typedef struct { + /* if set, then this setting class has no own fields. Instead, its + * data is entirely based on gendata. Meaning: it tracks all data + * as native GVariants. + * It might have some GObject properties, but these are merely accessors + * to the underlying gendata. + * + * Note, that at the moment there are few hooks, to customize the behavior + * of the setting further. They are currently unneeded. This is desired, + * but could be added when there is a good reason. + * + * However, a few hooks there are... see NMSettInfoSettGendata. */ + const NMSettInfoSettGendata *gendata_info; +} NMSettInfoSettDetail; + +struct _NMSettInfoSetting { NMSettingClass *setting_class; const NMSettInfoProperty *property_infos; guint property_infos_len; NMSettInfoSettDetail detail; -} NMSettInfoSetting; +}; const NMSettInfoSetting *_nm_sett_info_setting_get (NMSettingClass *setting_class); diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index 6ea42d13bc..3f9d60bbb7 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -2684,6 +2684,8 @@ read_one_setting_value (NMSetting *setting, static NMSetting * read_setting (KeyfileReaderInfo *info) { + const NMSettInfoSetting *sett_info; + gs_unref_object NMSetting *setting = NULL; const char *alias; GType type; @@ -2692,22 +2694,92 @@ read_setting (KeyfileReaderInfo *info) alias = info->group; type = nm_setting_lookup_type (alias); - if (type) { - NMSetting *setting = g_object_new (type, NULL); - - info->setting = setting; - nm_setting_enumerate_values (setting, read_one_setting_value, info); - info->setting = NULL; - if (!info->error) - return setting; - - g_object_unref (setting); - } else { + if (!type) { handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN, _("invalid setting name '%s'"), info->group); + return NULL; } - return NULL; + setting = g_object_new (type, NULL); + + info->setting = setting; + + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + + if (sett_info->detail.gendata_info) { + gs_free char **keys = NULL; + gsize i, n_keys; + + keys = g_key_file_get_keys (info->keyfile, info->group, &n_keys, NULL); + if (n_keys > 0) { + GHashTable *h = _nm_setting_gendata_hash (setting, TRUE); + + nm_utils_strv_sort (keys, n_keys); + for (i = 0; i < n_keys; i++) { + gs_free char *key = keys[i]; + gs_free_error GError *local = NULL; + const GVariantType *variant_type; + GVariant *variant; + + /* a GKeyfile can return duplicate keys, there is just no API to make sense + * of them. Skip them. */ + if ( i + 1 < n_keys + && nm_streq (key, keys[i + 1])) + continue; + + /* currently, the API is very simple. The setting class just returns + * the desired variant type, and keyfile reader will try to parse + * it accordingly. Note, that this does currently not allow, that + * a particular key can contain different variant types, nor is it + * very flexible in general. + * + * We add flexibility when we need it. Keep it simple for now. */ + variant_type = sett_info->detail.gendata_info->get_variant_type (sett_info, + key, + &local); + if (!variant_type) { + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key '%s.%s'"), + info->group, key)) + break; + continue; + } + + if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN)) { + gboolean v; + + v = g_key_file_get_boolean (info->keyfile, + info->group, + key, + &local); + if (local) { + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not boolean"), + info->group, key)) + break; + continue; + } + variant = g_variant_new_boolean (v); + } else { + nm_assert_not_reached (); + continue; + } + + g_hash_table_insert (h, + g_steal_pointer (&key), + g_variant_take_ref (variant)); + } + for (; i < n_keys; i++) + g_free (keys[i]); + } + } else + nm_setting_enumerate_values (setting, read_one_setting_value, info); + + info->setting = NULL; + + if (info->error) + return NULL; + return g_steal_pointer (&setting); } static void @@ -2998,6 +3070,8 @@ nm_keyfile_write (NMConnection *connection, GError **error) { KeyfileWriterInfo info = { 0 }; + gs_free NMSetting **settings = NULL; + guint i, length = 0; g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (!error || !*error, NULL); @@ -3010,12 +3084,59 @@ nm_keyfile_write (NMConnection *connection, info.error = NULL; info.handler = handler; info.user_data = user_data; - nm_connection_for_each_setting_value (connection, write_setting_value, &info); + + settings = nm_connection_get_settings (connection, &length); + for (i = 0; i < length; i++) { + const NMSettInfoSetting *sett_info; + NMSetting *setting = settings[i]; + + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + + if (sett_info->detail.gendata_info) { + guint k, n_keys; + const char *const*keys; + + nm_assert (!nm_keyfile_plugin_get_alias_for_setting_name (sett_info->setting_class->setting_info->setting_name)); + + n_keys = _nm_setting_gendata_get_all (setting, &keys, NULL); + + if (n_keys > 0) { + const char *setting_name = sett_info->setting_class->setting_info->setting_name; + GHashTable *h = _nm_setting_gendata_hash (setting, FALSE); + + for (k = 0; k < n_keys; k++) { + const char *key = keys[k]; + GVariant *v; + + v = g_hash_table_lookup (h, key); + + if (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) { + g_key_file_set_boolean (info.keyfile, + setting_name, + key, + g_variant_get_boolean (v)); + } else { + /* BUG: The variant type is not implemented. Since the connection + * verifies, this can only mean we either wrongly didn't reject + * the connection as invalid, or we didn't properly implement the + * variant type. */ + nm_assert_not_reached (); + continue; + } + } + } + } else + nm_setting_enumerate_values (setting, write_setting_value, &info); + + if (info.error) + break; + } if (info.error) { g_propagate_error (error, info.error); g_key_file_unref (info.keyfile); return NULL; } + return info.keyfile; } diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h index 99c7b69ccd..1e25226ede 100644 --- a/libnm-core/nm-setting-private.h +++ b/libnm-core/nm-setting-private.h @@ -80,6 +80,8 @@ gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting, */ #define NM_SETTING_PARAM_REAPPLY_IMMEDIATELY (1 << (6 + G_PARAM_USER_SHIFT)) +#define NM_SETTING_PARAM_GENDATA_BACKED (1 << (7 + G_PARAM_USER_SHIFT)) + GVariant *_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting, NMConnection *connection, const char *property); @@ -127,6 +129,15 @@ _nm_setting_class_commit (NMSettingClass *setting_class, _nm_setting_class_commit_full (setting_class, meta_type, NULL, NULL); } +#define NM_SETT_INFO_SETT_GENDATA(...) \ + ({ \ + static const NMSettInfoSettGendata _g = { \ + __VA_ARGS__ \ + }; \ + \ + &_g; \ + }) + #define NM_SETT_INFO_SETT_DETAIL(...) \ (&((const NMSettInfoSettDetail) { \ __VA_ARGS__ \ diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c index 2e884de7a0..53a2a22a16 100644 --- a/libnm-core/nm-setting.c +++ b/libnm-core/nm-setting.c @@ -56,6 +56,12 @@ /*****************************************************************************/ typedef struct { + GHashTable *hash; + const char **names; + GVariant **values; +} GenData; + +typedef struct { const char *name; GType type; NMSettingPriority priority; @@ -69,7 +75,7 @@ enum { }; typedef struct { - int dummy; + GenData *gendata; } NMSettingPrivate; G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT) @@ -78,6 +84,10 @@ G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT) /*****************************************************************************/ +static GenData *_gendata_hash (NMSetting *setting, gboolean create_if_necessary); + +/*****************************************************************************/ + static NMSettingPriority _get_base_type_priority (const NMMetaSettingInfo *setting_info, GType gtype) @@ -623,15 +633,27 @@ set_property_from_dbus (const NMSettInfoProperty *property, GVariant * _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags) { + NMSettingPrivate *priv; GVariantBuilder builder; GVariant *dbus_value; const NMSettInfoSetting *sett_info; - guint i; + guint n_properties, i; + const char *const*gendata_keys; g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + priv = NM_SETTING_GET_PRIVATE (setting); + g_variant_builder_init (&builder, NM_VARIANT_TYPE_SETTING); + n_properties = _nm_setting_gendata_get_all (setting, &gendata_keys, NULL); + for (i = 0; i < n_properties; i++) { + g_variant_builder_add (&builder, + "{sv}", + gendata_keys[i], + g_hash_table_lookup (priv->gendata->hash, gendata_keys[i])); + } + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); for (i = 0; i < sett_info->property_infos_len; i++) { const NMSettInfoProperty *property = &sett_info->property_infos[i]; @@ -647,6 +669,9 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionS if (!(prop_spec->flags & G_PARAM_WRITABLE)) continue; + if (NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_GENDATA_BACKED)) + continue; + if ( (prop_spec->flags & NM_SETTING_PARAM_LEGACY) && !_nm_utils_is_manager_process) continue; @@ -751,6 +776,26 @@ _nm_setting_new_from_dbus (GType setting_type, } sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + + if (sett_info->detail.gendata_info) { + GHashTable *hash; + GVariantIter iter; + char *key; + GVariant *val; + + hash = _gendata_hash (setting, TRUE)->hash; + + g_variant_iter_init (&iter, setting_dict); + while (g_variant_iter_next (&iter, "{sv}", &key, &val)) { + g_hash_table_insert (hash, + key, + val); + } + + _nm_setting_gendata_notify (setting, TRUE); + return g_steal_pointer (&setting); + } + for (i = 0; i < sett_info->property_infos_len; i++) { const NMSettInfoProperty *property = &sett_info->property_infos[i]; gs_unref_variant GVariant *value = NULL; @@ -888,14 +933,32 @@ nm_setting_get_dbus_property_type (NMSetting *setting, gboolean _nm_setting_get_property (NMSetting *setting, const char *property_name, GValue *value) { + const NMSettInfoSetting *sett_info; GParamSpec *prop_spec; g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); g_return_val_if_fail (property_name, FALSE); g_return_val_if_fail (value, FALSE); - prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name); + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + if (sett_info->detail.gendata_info) { + GVariant *variant; + GenData *gendata = _gendata_hash (setting, FALSE); + + variant = gendata ? g_hash_table_lookup (gendata->hash, property_name) : NULL; + + if (!variant) { + g_value_unset (value); + return FALSE; + } + + g_value_init (value, G_TYPE_VARIANT); + g_value_set_variant (value, variant); + return TRUE; + } + + prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name); if (!prop_spec) { g_value_unset (value); return FALSE; @@ -929,16 +992,37 @@ duplicate_setting (NMSetting *setting, NMSetting * nm_setting_duplicate (NMSetting *setting) { + const NMSettInfoSetting *sett_info; GObject *dup; g_return_val_if_fail (NM_IS_SETTING (setting), NULL); dup = g_object_new (G_OBJECT_TYPE (setting), NULL); - g_object_freeze_notify (dup); - nm_setting_enumerate_values (setting, duplicate_setting, dup); - g_object_thaw_notify (dup); + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + if (sett_info->detail.gendata_info) { + GenData *gendata = _gendata_hash (setting, FALSE); + + if ( gendata + && g_hash_table_size (gendata->hash) > 0) { + GHashTableIter iter; + GHashTable *h = _gendata_hash (NM_SETTING (dup), TRUE)->hash; + const char *key; + GVariant *val; + + g_hash_table_iter_init (&iter, gendata->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) { + g_hash_table_insert (h, + g_strdup (key), + g_variant_ref (val)); + } + } + } else { + g_object_freeze_notify (dup); + nm_setting_enumerate_values (setting, duplicate_setting, dup); + g_object_thaw_notify (dup); + } return NM_SETTING (dup); } @@ -1118,6 +1202,7 @@ nm_setting_compare (NMSetting *a, NMSetting *b, NMSettingCompareFlags flags) { + const NMSettInfoSetting *sett_info; GParamSpec **property_specs; guint n_property_specs; int same = TRUE; @@ -1130,6 +1215,18 @@ nm_setting_compare (NMSetting *a, if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b)) return FALSE; + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a)); + + if (sett_info->detail.gendata_info) { + GenData *a_gendata = _gendata_hash (a, FALSE); + GenData *b_gendata = _gendata_hash (b, FALSE); + + return nm_utils_hash_table_equal (a_gendata ? a_gendata->hash : NULL, + b_gendata ? b_gendata->hash : NULL, + TRUE, + g_variant_equal); + } + /* And now all properties */ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); for (i = 0; i < n_property_specs && same; i++) { @@ -1215,6 +1312,21 @@ should_compare_prop (NMSetting *setting, return TRUE; } +static void +_setting_diff_add_result (GHashTable *results, const char *prop_name, NMSettingDiffResult r) +{ + void *p; + + if (r == NM_SETTING_DIFF_RESULT_UNKNOWN) + return; + + if (g_hash_table_lookup_extended (results, prop_name, NULL, &p)) { + if (!NM_FLAGS_ALL ((guint) r, GPOINTER_TO_UINT (p))) + g_hash_table_insert (results, g_strdup (prop_name), GUINT_TO_POINTER (((guint) r) | GPOINTER_TO_UINT (p))); + } else + g_hash_table_insert (results, g_strdup (prop_name), GUINT_TO_POINTER (r)); +} + /** * nm_setting_diff: * @a: a #NMSetting @@ -1243,8 +1355,7 @@ nm_setting_diff (NMSetting *a, gboolean invert_results, GHashTable **results) { - GParamSpec **property_specs; - guint n_property_specs; + const NMSettInfoSetting *sett_info; guint i; NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A; NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B; @@ -1288,78 +1399,117 @@ nm_setting_diff (NMSetting *a, results_created = TRUE; } - /* And now all properties */ - property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a)); - for (i = 0; i < n_property_specs; i++) { - GParamSpec *prop_spec = property_specs[i]; - NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN; + if (sett_info->detail.gendata_info) { + const char *key; + GVariant *val, *val2; + GHashTableIter iter; + GenData *a_gendata = _gendata_hash (a, FALSE); + GenData *b_gendata = b ? _gendata_hash (b, FALSE) : NULL; + + if (!a_gendata || !b_gendata) { + if (a_gendata || b_gendata) { + NMSettingDiffResult one_sided_result; + + one_sided_result = a_gendata ? a_result : b_result; + g_hash_table_iter_init (&iter, a_gendata ? a_gendata->hash : b_gendata->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) { + diff_found = TRUE; + _setting_diff_add_result (*results, key, one_sided_result); + } + } + } else { + g_hash_table_iter_init (&iter, a_gendata->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) { + val2 = b_gendata ? g_hash_table_lookup (b_gendata->hash, key) : NULL; + compared_any = TRUE; + if ( !val2 + || !g_variant_equal (val, val2)) { + diff_found = TRUE; + _setting_diff_add_result (*results, key, a_result); + } + } + g_hash_table_iter_init (&iter, b_gendata->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) { + val2 = a_gendata ? g_hash_table_lookup (a_gendata->hash, key) : NULL; + compared_any = TRUE; + if ( !val2 + || !g_variant_equal (val, val2)) { + diff_found = TRUE; + _setting_diff_add_result (*results, key, b_result); + } + } + } + } else { + gs_free GParamSpec **property_specs = NULL; + guint n_property_specs; - /* Handle compare flags */ - if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags)) - continue; - if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0) - continue; + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); - compared_any = TRUE; + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN; - if (b) { - gboolean different; + /* Handle compare flags */ + if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags)) + continue; + if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0) + continue; - different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags); - if (different) { - gboolean a_is_default, b_is_default; + compared_any = TRUE; + + if (b) { + gboolean different; + + different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags); + if (different) { + gboolean a_is_default, b_is_default; + GValue value = G_VALUE_INIT; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (G_OBJECT (a), prop_spec->name, &value); + a_is_default = g_param_value_defaults (prop_spec, &value); + + g_value_reset (&value); + g_object_get_property (G_OBJECT (b), prop_spec->name, &value); + b_is_default = g_param_value_defaults (prop_spec, &value); + + g_value_unset (&value); + if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) { + if (!a_is_default) + r |= a_result; + if (!b_is_default) + r |= b_result; + } else { + r |= a_result | b_result; + if (a_is_default) + r |= a_result_default; + if (b_is_default) + r |= b_result_default; + } + } + } else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0) + r = a_result; /* only in A */ + else { GValue value = G_VALUE_INIT; g_value_init (&value, prop_spec->value_type); g_object_get_property (G_OBJECT (a), prop_spec->name, &value); - a_is_default = g_param_value_defaults (prop_spec, &value); - - g_value_reset (&value); - g_object_get_property (G_OBJECT (b), prop_spec->name, &value); - b_is_default = g_param_value_defaults (prop_spec, &value); + if (!g_param_value_defaults (prop_spec, &value)) + r |= a_result; + else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) + r |= a_result | a_result_default; g_value_unset (&value); - if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) { - if (!a_is_default) - r |= a_result; - if (!b_is_default) - r |= b_result; - } else { - r |= a_result | b_result; - if (a_is_default) - r |= a_result_default; - if (b_is_default) - r |= b_result_default; - } } - } else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0) - r = a_result; /* only in A */ - else { - 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; - else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) - r |= a_result | a_result_default; - g_value_unset (&value); - } - - if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) { - void *p; - - diff_found = TRUE; - if (g_hash_table_lookup_extended (*results, prop_spec->name, NULL, &p)) { - if ((r & GPOINTER_TO_UINT (p)) != r) - g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (r | GPOINTER_TO_UINT (p))); - } else - g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (r)); + if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) { + diff_found = TRUE; + _setting_diff_add_result (*results, prop_spec->name, r); + } } } - g_free (property_specs); if (!compared_any && !b) { /* special case: the setting has no properties, and the opposite @@ -1370,7 +1520,7 @@ nm_setting_diff (NMSetting *a, if (diff_found) { /* if there is a difference, we always return FALSE. It also means, we might - * have allocated a new @results hash, and return if to the caller. */ + * have allocated a new @results hash, and return it to the caller. */ return FALSE; } else { if (results_created) { @@ -1426,23 +1576,57 @@ nm_setting_enumerate_values (NMSetting *setting, NMSettingValueIterFn func, gpointer user_data) { + const NMSettInfoSetting *sett_info; GParamSpec **property_specs; - guint n_property_specs; - int i; + guint n_properties; + guint i; GType type; g_return_if_fail (NM_IS_SETTING (setting)); g_return_if_fail (func != NULL); - property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + + if (sett_info->detail.gendata_info) { + const char *const*names; + + /* the properties of this setting are not real GObject properties. + * Hence, this API makes little sense (or does it?). Still, call + * @func with each value. */ + n_properties = _nm_setting_gendata_get_all (setting, &names, NULL); + if (n_properties > 0) { + gs_strfreev char **keys = g_strdupv ((char **) names); + GHashTable *h = _gendata_hash (setting, FALSE)->hash; + + for (i = 0; i < n_properties; i++) { + GValue value = G_VALUE_INIT; + GVariant *val = g_hash_table_lookup (h, keys[i]); + + if (!val) { + /* was deleted in the meantime? Skip */ + continue; + } + + g_value_init (&value, G_TYPE_VARIANT); + g_value_set_variant (&value, val); + /* call it will GParamFlags 0. It shall indicate that this + * is not a "real" GObject property. */ + func (setting, keys[i], &value, 0, user_data); + g_value_unset (&value); + } + } + return; + } + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_properties); /* sort the properties. This has an effect on the order in which keyfile * prints them. */ type = G_OBJECT_TYPE (setting); - g_qsort_with_data (property_specs, n_property_specs, sizeof (gpointer), + g_qsort_with_data (property_specs, n_properties, sizeof (gpointer), (GCompareDataFunc) _enumerate_values_sort, &type); - for (i = 0; i < n_property_specs; i++) { + for (i = 0; i < n_properties; i++) { GParamSpec *prop_spec = property_specs[i]; GValue value = G_VALUE_INIT; @@ -1468,7 +1652,7 @@ nm_setting_enumerate_values (NMSetting *setting, gboolean _nm_setting_clear_secrets (NMSetting *setting) { - GParamSpec **property_specs; + gs_free GParamSpec **property_specs = NULL; guint n_property_specs; guint i; gboolean changed = FALSE; @@ -1476,7 +1660,6 @@ _nm_setting_clear_secrets (NMSetting *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]; @@ -1493,9 +1676,6 @@ _nm_setting_clear_secrets (NMSetting *setting) g_value_unset (&value); } } - - g_free (property_specs); - return changed; } @@ -1546,7 +1726,7 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting, NMSettingClearSecretsWithFlagsFn func, gpointer user_data) { - GParamSpec **property_specs; + gs_free GParamSpec **property_specs = NULL; guint n_property_specs; guint i; gboolean changed = FALSE; @@ -1564,8 +1744,6 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting, user_data); } } - - g_free (property_specs); return changed; } @@ -1870,6 +2048,240 @@ _nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting, /*****************************************************************************/ +static GenData * +_gendata_hash (NMSetting *setting, gboolean create_if_necessary) +{ + NMSettingPrivate *priv; + + nm_assert (NM_IS_SETTING (setting)); + + priv = NM_SETTING_GET_PRIVATE (setting); + + if (G_UNLIKELY (!priv->gendata)) { + if (!create_if_necessary) + return NULL; + priv->gendata = g_slice_new (GenData); + priv->gendata->hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); + priv->gendata->names = NULL; + priv->gendata->values = NULL; + } + + return priv->gendata; +} + +GHashTable * +_nm_setting_gendata_hash (NMSetting *setting, gboolean create_if_necessary) +{ + GenData *gendata; + + gendata = _gendata_hash (setting, create_if_necessary); + return gendata ? gendata->hash : NULL; +} + +void +_nm_setting_gendata_notify (NMSetting *setting, + gboolean names_changed) +{ + GenData *gendata; + + gendata = _gendata_hash (setting, FALSE); + if (!gendata) + return; + + nm_clear_g_free (&gendata->values); + + if (names_changed) { + /* if only the values changed, it's sufficient to invalidate the + * values cache. Otherwise, the names cache must be invalidated too. */ + nm_clear_g_free (&gendata->names); + } +} + +GVariant * +nm_setting_gendata_get (NMSetting *setting, + const char *name) +{ + GenData *gendata; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + g_return_val_if_fail (name, NULL); + + gendata = _gendata_hash (setting, FALSE); + return gendata ? g_hash_table_lookup (gendata->hash, name) : NULL; +} + +guint +_nm_setting_gendata_get_all (NMSetting *setting, + const char *const**out_names, + GVariant *const**out_values) +{ + GenData *gendata; + GHashTable *hash; + guint i, len; + + nm_assert (NM_IS_SETTING (setting)); + + gendata = _gendata_hash (setting, FALSE); + if (!gendata) + goto out_zero; + + hash = gendata->hash; + len = g_hash_table_size (hash); + if (len == 0) + goto out_zero; + + if (!out_names && !out_values) + return len; + + if (G_UNLIKELY (!gendata->names)) { + gendata->names = nm_utils_strdict_get_keys (hash, + TRUE, + NULL); + } + + if (out_values) { + if (G_UNLIKELY (!gendata->values)) { + gendata->values = g_new (GVariant *, len + 1); + for (i = 0; i < len; i++) + gendata->values[i] = g_hash_table_lookup (hash, gendata->names[i]); + gendata->values[i] = NULL; + } + *out_values = gendata->values; + } + + NM_SET_OUT (out_names, (const char *const*) gendata->names); + return len; + +out_zero: + NM_SET_OUT (out_names, NULL); + NM_SET_OUT (out_values, NULL); + return 0; +} + +/** + * nm_setting_gendata_get_all_names: + * @setting: the #NMSetting + * @out_len: (allow-none): (out): + * + * Gives the number of generic data elements and optionally returns all their + * key names and values. This API is low level access and unless you know what you + * are doing, it might not be what you want. + * + * Returns: (array length=out_len zero-terminated=1) (transfer none): + * A %NULL terminated array of key names. If no names are present, this returns + * %NULL. The returned array and the names are owned by %NMSetting and might be invalidated + * soon. + * + * Since: 1.14 + **/ +const char *const* +nm_setting_gendata_get_all_names (NMSetting *setting, + guint *out_len) +{ + const char *const*names; + guint len; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + len = _nm_setting_gendata_get_all (setting, &names, NULL); + NM_SET_OUT (out_len, len); + return names; +} + +/** + * nm_setting_gendata_get_all_values: + * @setting: the #NMSetting + * + * Gives the number of generic data elements and optionally returns all their + * key names and values. This API is low level access and unless you know what you + * are doing, it might not be what you want. + * + * Returns: (array zero-terminated=1) (transfer none): + * A %NULL terminated array of #GVariant. If no data is present, this returns + * %NULL. The returned array and the variants are owned by %NMSetting and might be invalidated + * soon. The sort order of nm_setting_gendata_get_all_names() and nm_setting_gendata_get_all_values() + * is consistent. That means, the nth value has the nth name returned by nm_setting_gendata_get_all_names(). + * + * Since: 1.14 + **/ +GVariant *const* +nm_setting_gendata_get_all_values (NMSetting *setting) +{ + GVariant *const*values; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + _nm_setting_gendata_get_all (setting, NULL, &values); + return values; +} + +void +_nm_setting_gendata_to_gvalue (NMSetting *setting, + GValue *value) +{ + GenData *gendata; + GHashTable *new; + const char *key; + GVariant *val; + GHashTableIter iter; + + nm_assert (NM_IS_SETTING (setting)); + nm_assert (value); + nm_assert (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_HASH_TABLE)); + + new = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); + + gendata = _gendata_hash (setting, FALSE); + if (gendata) { + g_hash_table_iter_init (&iter, gendata->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert (new, g_strdup (key), g_variant_ref (val)); + } + + g_value_take_boxed (value, new); +} + +gboolean +_nm_setting_gendata_reset_from_hash (NMSetting *setting, + GHashTable *new) +{ + GenData *gendata; + GHashTableIter iter; + const char *key; + GVariant *val; + guint num; + + nm_assert (NM_IS_SETTING (setting)); + nm_assert (new); + + num = new ? g_hash_table_size (new) : 0; + + gendata = _gendata_hash (setting, num > 0); + + if (num == 0) { + if ( !gendata + || g_hash_table_size (gendata->hash) == 0) + return FALSE; + + g_hash_table_remove_all (gendata->hash); + _nm_setting_gendata_notify (setting, TRUE); + return TRUE; + } + + /* let's not bother to find out whether the new hash has any different + * content the the current gendata. Just replace it. */ + g_hash_table_remove_all (gendata->hash); + if (num > 0) { + g_hash_table_iter_init (&iter, new); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert (gendata->hash, g_strdup (key), g_variant_ref (val)); + } + _nm_setting_gendata_notify (setting, TRUE); + return TRUE; +} + +/*****************************************************************************/ + static void nm_setting_init (NMSetting *setting) { @@ -1892,6 +2304,21 @@ get_property (GObject *object, guint prop_id, } static void +finalize (GObject *object) +{ + NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object); + + if (priv->gendata) { + g_free (priv->gendata->names); + g_free (priv->gendata->values); + g_hash_table_unref (priv->gendata->hash); + g_slice_free (GenData, priv->gendata); + } + + G_OBJECT_CLASS (nm_setting_parent_class)->finalize (object); +} + +static void nm_setting_class_init (NMSettingClass *setting_class) { GObjectClass *object_class = G_OBJECT_CLASS (setting_class); @@ -1914,6 +2341,7 @@ nm_setting_class_init (NMSettingClass *setting_class) g_type_class_add_private (setting_class, sizeof (NMSettingPrivate)); 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; diff --git a/libnm-core/nm-setting.h b/libnm-core/nm-setting.h index d06527cdd7..a7a0f81f40 100644 --- a/libnm-core/nm-setting.h +++ b/libnm-core/nm-setting.h @@ -287,7 +287,8 @@ void nm_setting_enumerate_values (NMSetting *setting, char *nm_setting_to_string (NMSetting *setting); -/* Secrets */ +/*****************************************************************************/ + gboolean nm_setting_get_secret_flags (NMSetting *setting, const char *secret_name, NMSettingSecretFlags *out_flags, @@ -298,10 +299,13 @@ gboolean nm_setting_set_secret_flags (NMSetting *setting, NMSettingSecretFlags flags, GError **error); -/* Properties */ +/*****************************************************************************/ + const GVariantType *nm_setting_get_dbus_property_type (NMSetting *setting, const char *property_name); +/*****************************************************************************/ + G_END_DECLS #endif /* __NM_SETTING_H__ */ |