diff options
author | Thomas Haller <thaller@redhat.com> | 2019-01-07 11:39:46 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-01-07 11:39:46 +0100 |
commit | 0fdcbc6bfa9744f4c5fe40815d1f51cd64289ebe (patch) | |
tree | 8c15cea9d482d05855bdc3a375c72ab171fbbb20 | |
parent | 22dc82724cba5c2922b3f6663a8a78605cbc3e3c (diff) | |
parent | a5b20ba211f62c4ef29cb34d2a800291ae37e31d (diff) | |
download | NetworkManager-0fdcbc6bfa9744f4c5fe40815d1f51cd64289ebe.tar.gz |
libnm: merge branch 'th/keyfile-fixes'
https://github.com/NetworkManager/NetworkManager/pull/273
26 files changed, 1553 insertions, 553 deletions
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index eb42bc07a4..9e5de7b267 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -2034,6 +2034,73 @@ nm_connection_for_each_setting_value (NMConnection *connection, } /** + * _nm_connection_aggregate: + * @connecition: the #NMConnection for which values are to be aggregated. + * @type: one of the supported aggrate types. + * @arg: the input/output argument that depends on @type. + * + * For example, with %NM_CONNECTION_AGGREGATE_ANY_SECRETS and + * %NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS @arg is a boolean + * output argument. It is either %NULL or a pointer to an gboolean + * out-argument. The function will always set @arg if given. + * Also, the return value of the function is likewise the result + * that is set to @arg. + * + * Returns: a boolean result with the meaning depending on the aggregation + * type @type. + */ +gboolean +_nm_connection_aggregate (NMConnection *connection, + NMConnectionAggregateType type, + gpointer arg) +{ + NMConnectionPrivate *priv; + GHashTableIter iter; + NMSetting *setting; + gboolean arg_boolean; + gboolean completed_early; + gpointer my_arg; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + switch (type) { + case NM_CONNECTION_AGGREGATE_ANY_SECRETS: + arg_boolean = FALSE; + my_arg = &arg_boolean; + goto good; + case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS: + arg_boolean = FALSE; + my_arg = &arg_boolean; + goto good; + } + g_return_val_if_reached (FALSE); + +good: + priv = NM_CONNECTION_GET_PRIVATE (connection); + + completed_early = FALSE; + g_hash_table_iter_init (&iter, priv->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) { + if (_nm_setting_aggregate (setting, type, my_arg)) { + completed_early = TRUE; + break; + } + nm_assert ( my_arg != &arg_boolean + || !arg_boolean); + } + + if (my_arg == &arg_boolean) { + nm_assert (completed_early == arg_boolean); + if (arg) + *((gboolean *) arg) = arg_boolean; + return arg_boolean; + } + + nm_assert_not_reached (); + return FALSE; +} + +/** * nm_connection_dump: * @connection: the #NMConnection * diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h index fb1f901cc6..8d64a4cebc 100644 --- a/libnm-core/nm-connection.h +++ b/libnm-core/nm-connection.h @@ -121,8 +121,6 @@ typedef enum { /*< flags >*/ NM_CONNECTION_SERIALIZE_ALL = 0x00000000, NM_CONNECTION_SERIALIZE_NO_SECRETS = 0x00000001, NM_CONNECTION_SERIALIZE_ONLY_SECRETS = 0x00000002, - - /* 0x80000000 is used for a private flag */ } NMConnectionSerializationFlags; GVariant *nm_connection_to_dbus (NMConnection *connection, diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index ff2444673f..1e7ec9bcbc 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -121,11 +121,21 @@ */ #define NM_SETTING_COMPARE_FLAG_NONE ((NMSettingCompareFlags) 0) +/*****************************************************************************/ + #define NM_SETTING_SECRET_FLAGS_ALL \ - (NM_SETTING_SECRET_FLAG_NONE | \ - NM_SETTING_SECRET_FLAG_AGENT_OWNED | \ - NM_SETTING_SECRET_FLAG_NOT_SAVED | \ - NM_SETTING_SECRET_FLAG_NOT_REQUIRED) + ((NMSettingSecretFlags) ( NM_SETTING_SECRET_FLAG_NONE \ + | NM_SETTING_SECRET_FLAG_AGENT_OWNED \ + | NM_SETTING_SECRET_FLAG_NOT_SAVED \ + | NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) + +static inline gboolean +_nm_setting_secret_flags_valid (NMSettingSecretFlags flags) +{ + return !NM_FLAGS_ANY (flags, ~NM_SETTING_SECRET_FLAGS_ALL); +} + +/*****************************************************************************/ typedef enum { /*< skip >*/ NM_SETTING_PARSE_FLAGS_NONE = 0, @@ -146,6 +156,26 @@ gpointer _nm_connection_check_main_setting (NMConnection *connection, const char *setting_name, GError **error); +typedef enum { + /* whether the connection has any secrets. + * + * @arg may be %NULL or a pointer to a gboolean for the result. The return + * value of _nm_connection_aggregate() is likewise the boolean result. */ + NM_CONNECTION_AGGREGATE_ANY_SECRETS, + + /* whether the connection has any secret with flags NM_SETTING_SECRET_FLAG_NONE. + * Note that this only cares about the flags, not whether the secret is actually + * present. + * + * @arg may be %NULL or a pointer to a gboolean for the result. The return + * value of _nm_connection_aggregate() is likewise the boolean result. */ + NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, +} NMConnectionAggregateType; + +gboolean _nm_connection_aggregate (NMConnection *connection, + NMConnectionAggregateType type, + gpointer arg); + /** * NMSettingVerifyResult: * @NM_SETTING_VERIFY_SUCCESS: the setting verifies successfully @@ -174,13 +204,6 @@ NMSettingPriority _nm_setting_get_setting_priority (NMSetting *setting); gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value); -/* NM_CONNECTION_SERIALIZE_NO_SYNTH: This flag is passed to _nm_setting_to_dbus() - * by nm_setting_to_string() to let it know that it shouldn't serialize the - * synthetic properties. It wouldn't be able to do so, since the full connection - * is not available, only the setting alone. - */ -#define NM_CONNECTION_SERIALIZE_NO_SYNTH ((NMConnectionSerializationFlags) 0x80000000) - /*****************************************************************************/ GHashTable *_nm_setting_gendata_hash (NMSetting *setting, @@ -589,9 +612,11 @@ typedef struct _NMSettInfoSetting NMSettInfoSetting; typedef GVariant *(*NMSettingPropertyGetFunc) (NMSetting *setting, const char *property); -typedef GVariant *(*NMSettingPropertySynthFunc) (NMSetting *setting, +typedef GVariant *(*NMSettingPropertySynthFunc) (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property); + NMSetting *setting, + NMConnectionSerializationFlags flags); typedef gboolean (*NMSettingPropertySetFunc) (NMSetting *setting, GVariant *connection_dict, const char *property, @@ -644,11 +669,39 @@ typedef struct { struct _NMSettInfoSetting { NMSettingClass *setting_class; + + /* the properties, sorted by property name. */ const NMSettInfoProperty *property_infos; + + /* the @property_infos list is sorted by property name. For some uses we need + * a different sort order. If @property_infos_sorted is set, this is the order + * instead. It is used for: + * + * - nm_setting_enumerate_values() + * - keyfile writer adding keys to the group. + * + * Note that currently only NMSettingConnection implements here a sort order + * that differs from alphabetical sort of the property names. + */ + const NMSettInfoProperty *const*property_infos_sorted; + guint property_infos_len; NMSettInfoSettDetail detail; }; +static inline const NMSettInfoProperty * +_nm_sett_info_property_info_get_sorted (const NMSettInfoSetting *sett_info, + guint idx) +{ + nm_assert (sett_info); + nm_assert (idx < sett_info->property_infos_len); + nm_assert (!sett_info->property_infos_sorted || sett_info->property_infos_sorted[idx]); + + return sett_info->property_infos_sorted + ? sett_info->property_infos_sorted[idx] + : &sett_info->property_infos[idx]; +} + const NMSettInfoSetting *_nm_sett_info_setting_get (NMSettingClass *setting_class); const NMSettInfoProperty *_nm_sett_info_property_get (NMSettingClass *setting_class, diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c index ff12fa1063..9543ebde6e 100644 --- a/libnm-core/nm-keyfile-utils.c +++ b/libnm-core/nm-keyfile-utils.c @@ -179,11 +179,41 @@ nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \ } DEFINE_KF_WRAPPER(string, char*, const char*); -DEFINE_KF_WRAPPER(integer, int, int); -DEFINE_KF_WRAPPER(uint64, guint64, guint64); DEFINE_KF_WRAPPER(boolean, gboolean, gboolean); DEFINE_KF_WRAPPER(value, char*, const char*); +gint64 +nm_keyfile_plugin_kf_get_int64 (GKeyFile *kf, + const char *group, + const char *key, + guint base, + gint64 min, + gint64 max, + gint64 fallback, + GError **error) +{ + gs_free char *s = NULL; + int errsv; + gint64 v; + + s = nm_keyfile_plugin_kf_get_value (kf, group, key, error); + if (!s) { + errno = ENODATA; + return fallback; + } + + v = _nm_utils_ascii_str_to_int64 (s, base, min, max, fallback); + errsv = errno; + if ( errsv != 0 + && error) { + g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value is not an integer in range [%lld, %lld]"), + (long long) min, (long long) max); + errno = errsv; + } + return v; +} + char ** nm_keyfile_plugin_kf_get_keys (GKeyFile *kf, const char *group, diff --git a/libnm-core/nm-keyfile-utils.h b/libnm-core/nm-keyfile-utils.h index 18d5dce831..0467230e49 100644 --- a/libnm-core/nm-keyfile-utils.h +++ b/libnm-core/nm-keyfile-utils.h @@ -67,12 +67,19 @@ void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \ const char *key, \ set_ctype value); DEFINE_KF_WRAPPER_PROTO(string, char*, const char*) -DEFINE_KF_WRAPPER_PROTO(integer, int, int) -DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64) DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean) DEFINE_KF_WRAPPER_PROTO(value, char*, const char*) /* Misc */ +gint64 nm_keyfile_plugin_kf_get_int64 (GKeyFile *kf, + const char *group, + const char *key, + guint base, + gint64 min, + gint64 max, + gint64 fallback, + GError **error); + char ** nm_keyfile_plugin_kf_get_keys (GKeyFile *kf, const char *group, gsize *out_length, diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index cec09c9f38..86a80e1dc7 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -96,6 +96,15 @@ _handle_warn (KeyfileReaderInfo *info, _info->error == NULL; \ }) +/*****************************************************************************/ + +static gboolean +_secret_flags_persist_secret (NMSettingSecretFlags flags) +{ + return flags == NM_SETTING_SECRET_FLAG_NONE; +} + +/*****************************************************************************/ /* Some setting properties also contain setting names, such as * NMSettingConnection's 'type' property (which specifies the base type of the * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave @@ -1375,55 +1384,119 @@ cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) g_object_set (setting, key, bytes, NULL); } +static int +_parity_from_char (int ch) +{ +#if NM_MORE_ASSERTS > 5 + { + static char check = 0; + + if (check == 0) { + nm_auto_unref_gtypeclass GEnumClass *klass = g_type_class_ref (NM_TYPE_SETTING_SERIAL_PARITY); + guint i; + + check = 1; + + /* In older versions, parity was G_TYPE_CHAR/gint8, and the character + * value was stored as integer. + * For example parity=69 equals parity=E, meaning NM_SETTING_SERIAL_PARITY_EVEN. + * + * That means, certain values are reserved. Assert that these numbers + * are not reused when we extend NMSettingSerialParity enum. + * Actually, since NM_SETTING_SERIAL_PARITY is g_param_spec_enum(), + * we anyway cannot extend the enum without breaking API... + * + * [1] commit "a91e60902e libnm-core: make NMSettingSerial:parity an enum" + * [2] https://cgit.freedesktop.org/NetworkManager/NetworkManager/commit/?id=a91e60902eabae1de93d61323dae6ac894b5d40f + */ + g_assert (G_IS_ENUM_CLASS (klass)); + for (i = 0; i < klass->n_values; i++) { + const GEnumValue *v = &klass->values[i]; + int num = v->value; + + g_assert (_parity_from_char (num) == -1); + g_assert (!NM_IN_SET (num, 'e', 'E', 'o', 'O', 'n', 'N')); + } + } + } +#endif + + switch (ch) { + case 'E': + case 'e': + return NM_SETTING_SERIAL_PARITY_EVEN; + case 'O': + case 'o': + return NM_SETTING_SERIAL_PARITY_ODD; + case 'N': + case 'n': + return NM_SETTING_SERIAL_PARITY_NONE; + } + + return -1; +} + static void parity_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); - NMSettingSerialParity parity; - int int_val; - gs_free char *str_val = NULL; + gs_free_error GError *err = NULL; + int parity; + gs_free char *tmp_str = NULL; + gint64 i64; /* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'. * We now accept either that or the (case-insensitive) character itself (but * still always write it the old way, for backward compatibility). */ - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); - if (!int_val) { - str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - if (str_val) { - if (str_val[0] && !str_val[1]) - int_val = str_val[0]; - else { - /* This will hit the warning below */ - int_val = 'X'; - } + tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, &err); + if (err) + goto out_err; + + if ( tmp_str + && tmp_str[0] != '\0' + && tmp_str[1] == '\0') { + /* the ASCII characters like 'E' are taken directly... */ + parity = _parity_from_char (tmp_str[0]); + if (parity >= 0) + goto parity_good; + } + + i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64); + if ( i64 != G_MININT64 + && errno == 0) { + + if ((parity = _parity_from_char (i64)) >= 0) { + /* another oddity: the string is a valid number. However, if the numeric values + * is one of the supported ASCII codes, accept it (like 69 for 'E'). + */ + goto parity_good; } + + /* Finally, take the numeric value as is. */ + parity = i64; + goto parity_good; } - if (!int_val) - return; + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid parity value '%s'"), + tmp_str ?: ""); + return; - switch (int_val) { - case 'E': - case 'e': - parity = NM_SETTING_SERIAL_PARITY_EVEN; - break; - case 'O': - case 'o': - parity = NM_SETTING_SERIAL_PARITY_ODD; - break; - case 'N': - case 'n': - parity = NM_SETTING_SERIAL_PARITY_NONE; - break; - default: - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid parity value '%s'"), - str_val ?: ""); +parity_good: + nm_g_object_set_property_enum (G_OBJECT (setting), key, NM_TYPE_SETTING_SERIAL_PARITY, parity, &err); + +out_err: + if (!err) + return; + if ( err->domain == G_KEY_FILE_ERROR + && NM_IN_SET (err->code, G_KEY_FILE_ERROR_GROUP_NOT_FOUND, + G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { + /* ignore such errors. The key is not present. */ return; } - - g_object_set (setting, key, parity, NULL); + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting: %s"), err->message); } static void @@ -1863,8 +1936,8 @@ write_hash_of_string (GKeyFile *file, keys = nm_utils_strdict_get_keys (hash, TRUE, &l); for (i = 0; i < l; i++) { + gs_free char *to_free = NULL; const char *property, *data; - gboolean write_item = TRUE; property = keys[i]; @@ -1875,19 +1948,16 @@ write_hash_of_string (GKeyFile *file, if (vpn_secrets) { NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - nm_setting_get_secret_flags (setting, property, &secret_flags, NULL); - if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) - write_item = FALSE; + if (!nm_setting_get_secret_flags (setting, property, &secret_flags, NULL)) + nm_assert_not_reached (); + if (!_secret_flags_persist_secret (secret_flags)) + continue; } - if (write_item) { - gs_free char *to_free = NULL; - - data = g_hash_table_lookup (hash, property); - nm_keyfile_plugin_kf_set_string (file, group_name, - nm_keyfile_key_encode (property, &to_free), - data); - } + data = g_hash_table_lookup (hash, property); + nm_keyfile_plugin_kf_set_string (file, group_name, + nm_keyfile_key_encode (property, &to_free), + data); } } @@ -2518,26 +2588,28 @@ _parse_info_find (NMSetting *setting, /*****************************************************************************/ static void -read_one_setting_value (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) +read_one_setting_value (KeyfileReaderInfo *info, + NMSetting *setting, + const NMSettInfoProperty *property_info) { - KeyfileReaderInfo *info = user_data; GKeyFile *keyfile = info->keyfile; - const char *setting_name; - int errsv; - GType type; gs_free_error GError *err = NULL; const ParseInfoProperty *pip; + gs_free char *tmp_str = NULL; + const char *setting_name; + const char *key; + GType type; + guint64 u64; + gint64 i64; - if (info->error) - return; + nm_assert (!info->error); + nm_assert (property_info->param_spec); - if (!(flags & G_PARAM_WRITABLE)) + if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE) return; + key = property_info->param_spec->name; + pip = _parse_info_find (setting, key, &setting_name); nm_assert (setting_name); @@ -2571,67 +2643,76 @@ read_one_setting_value (NMSetting *setting, return; } - type = G_VALUE_TYPE (value); + type = G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec); if (type == G_TYPE_STRING) { gs_free char *str_val = NULL; - str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); - g_object_set (setting, key, str_val, NULL); + str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, &err); + if (!err) + nm_g_object_set_property_string_take (G_OBJECT (setting), key, g_steal_pointer (&str_val), &err); } else if (type == G_TYPE_UINT) { - int int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); - if (int_val < 0) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid negative value (%i)"), - int_val)) - return; + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); + if (!err) { + u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64); + if ( u64 == G_MAXUINT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_uint (G_OBJECT (setting), key, u64, &err); } - g_object_set (setting, key, int_val, NULL); } else if (type == G_TYPE_INT) { - int int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); - g_object_set (setting, key, int_val, NULL); + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); + if (!err) { + i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64); + if ( i64 == G_MININT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_int (G_OBJECT (setting), key, i64, &err); + } } else if (type == G_TYPE_BOOLEAN) { gboolean bool_val; - bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, NULL); - g_object_set (setting, key, bool_val, NULL); + bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, &err); + if (!err) + nm_g_object_set_property_boolean (G_OBJECT (setting), key, bool_val, &err); } else if (type == G_TYPE_CHAR) { - int int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); - if (int_val < G_MININT8 || int_val > G_MAXINT8) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid char value (%i)"), - int_val)) - return; + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); + if (!err) { + /* As documented by glib, G_TYPE_CHAR is really a (signed!) gint8. */ + i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT8, G_MAXINT8, G_MININT64); + if ( i64 == G_MININT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_char (G_OBJECT (setting), key, i64, &err); } - - g_object_set (setting, key, int_val, NULL); } else if (type == G_TYPE_UINT64) { - gs_free char *tmp_str = NULL; - guint64 uint_val; - - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL); - uint_val = g_ascii_strtoull (tmp_str, NULL, 10); - g_object_set (setting, key, uint_val, NULL); + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); + if (!err) { + u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT64, G_MAXUINT64); + if ( u64 == G_MAXUINT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_uint64 (G_OBJECT (setting), key, u64, &err); + } } else if (type == G_TYPE_INT64) { - gs_free char *tmp_str = NULL; - gint64 int_val; - - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL); - int_val = _nm_utils_ascii_str_to_int64 (tmp_str, 10, G_MININT64, G_MAXINT64, 0); - errsv = errno; - if (errsv) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid int64 value (%s)"), - tmp_str)) - return; - } else - g_object_set (setting, key, int_val, NULL); + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); + if (!err) { + i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT64, G_MAXINT64, G_MAXINT64); + if ( i64 == G_MAXINT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_int64 (G_OBJECT (setting), key, i64, &err); + } } else if (type == G_TYPE_BYTES) { gs_free int *tmp = NULL; GByteArray *array; @@ -2673,32 +2754,40 @@ read_one_setting_value (NMSetting *setting, read_hash_of_string (keyfile, setting, key); } else if (type == G_TYPE_ARRAY) { read_array_of_uint (keyfile, setting, key); - } else if (G_VALUE_HOLDS_FLAGS (value)) { - guint64 uint_val; - - /* Flags are guint but GKeyFile has no uint reader, just uint64 */ - uint_val = nm_keyfile_plugin_kf_get_uint64 (keyfile, setting_name, key, &err); + } else if (G_TYPE_IS_FLAGS (type)) { + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); if (!err) { - if (uint_val <= G_MAXUINT) - g_object_set (setting, key, (guint) uint_val, NULL); - else { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("too large FLAGS property '%s' (%llu)"), - G_VALUE_TYPE_NAME (value), (unsigned long long) uint_val)) - return; - } + u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64); + if ( u64 == G_MAXUINT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_flags (G_OBJECT (setting), key, type, u64, &err); + } + } else if (G_TYPE_IS_ENUM (type)) { + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err); + if (!err) { + i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MAXINT64); + if ( i64 == G_MAXINT64 + && errno != 0) { + g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, + _("value cannot be interpreted as integer")); + } else + nm_g_object_set_property_enum (G_OBJECT (setting), key, type, i64, &err); } - } else if (G_VALUE_HOLDS_ENUM (value)) { - int int_val; + } else + g_return_if_reached (); - int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err); - if (!err) - g_object_set (setting, key, (int) int_val, NULL); - } else { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("unhandled setting property type '%s'"), - G_VALUE_TYPE_NAME (value))) - return; + if (err) { + if ( err->domain == G_KEY_FILE_ERROR + && NM_IN_SET (err->code, G_KEY_FILE_ERROR_GROUP_NOT_FOUND, + G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { + /* ignore such errors. The key is not present. */ + } else { + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting: %s"), err->message); + } } } @@ -2709,6 +2798,7 @@ _read_setting (KeyfileReaderInfo *info) gs_unref_object NMSetting *setting = NULL; const char *alias; GType type; + guint i; alias = nm_keyfile_plugin_get_setting_name_for_alias (info->group); if (!alias) @@ -2729,7 +2819,7 @@ _read_setting (KeyfileReaderInfo *info) if (sett_info->detail.gendata_info) { gs_free char **keys = NULL; - gsize i, n_keys; + gsize k, n_keys; keys = g_key_file_get_keys (info->keyfile, info->group, &n_keys, NULL); if (!keys) @@ -2738,16 +2828,16 @@ _read_setting (KeyfileReaderInfo *info) 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]; + for (k = 0; k < n_keys; k++) { + gs_free char *key = keys[k]; 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])) + if ( k + 1 < n_keys + && nm_streq (key, keys[k + 1])) continue; /* currently, the API is very simple. The setting class just returns @@ -2792,13 +2882,20 @@ _read_setting (KeyfileReaderInfo *info) g_steal_pointer (&key), g_variant_take_ref (variant)); } - for (; i < n_keys; i++) - g_free (keys[i]); + for (; k < n_keys; k++) + g_free (keys[k]); } - goto out; } - nm_setting_enumerate_values (setting, read_one_setting_value, info); + for (i = 0; i < sett_info->property_infos_len; i++) { + const NMSettInfoProperty *property_info = &sett_info->property_infos[i]; + + if (property_info->param_spec) { + read_one_setting_value (info, setting, property_info); + if (info->error) + goto out; + } + } out: info->setting = NULL; @@ -2974,23 +3071,23 @@ out_with_info_error: /*****************************************************************************/ static void -write_setting_value (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flag, - gpointer user_data) +write_setting_value (KeyfileWriterInfo *info, + NMSetting *setting, + const NMSettInfoProperty *property_info) { - KeyfileWriterInfo *info = user_data; + const ParseInfoProperty *pip; const char *setting_name; + const char *key; + char numstr[64]; + GValue value; GType type; - const ParseInfoProperty *pip; - GParamSpec *pspec; - if (info->error) + nm_assert (!info->error); + + if (!property_info->param_spec) return; - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key); - nm_assert (pspec); + key = property_info->param_spec->name; pip = _parse_info_find (setting, key, &setting_name); @@ -3018,58 +3115,65 @@ write_setting_value (NMSetting *setting, * the secret flags there are in a third-level hash in the 'secrets' * property. */ - if ( (pspec->flags & NM_SETTING_PARAM_SECRET) + if ( (property_info->param_spec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) { NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) - g_assert_not_reached (); - if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) + g_return_if_reached (); + if (!_secret_flags_persist_secret (secret_flags)) return; } + value = (GValue) { 0 }; + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec)); + g_object_get_property (G_OBJECT (setting), property_info->param_spec->name, &value); + if ( (!pip || !pip->writer_persist_default) - && g_param_value_defaults (pspec, (GValue *) value)) { + && g_param_value_defaults (property_info->param_spec, &value)) { nm_assert (!g_key_file_has_key (info->keyfile, setting_name, key, NULL)); - return; + goto out_unset_value; } if (pip && pip->writer) { - pip->writer (info, setting, key, value); - return; + pip->writer (info, setting, key, &value); + goto out_unset_value; } - type = G_VALUE_TYPE (value); + type = G_VALUE_TYPE (&value); if (type == G_TYPE_STRING) { const char *str; - str = g_value_get_string (value); + str = g_value_get_string (&value); if (str) nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str); - } else if (type == G_TYPE_UINT) - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value)); - else if (type == G_TYPE_INT) - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value)); - else if (type == G_TYPE_UINT64) { - char numstr[30]; - - nm_sprintf_buf (numstr, "%" G_GUINT64_FORMAT, g_value_get_uint64 (value)); + } else if (type == G_TYPE_UINT) { + nm_sprintf_buf (numstr, "%u", g_value_get_uint (&value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); + } else if (type == G_TYPE_INT) { + nm_sprintf_buf (numstr, "%d", g_value_get_int (&value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); + } else if (type == G_TYPE_UINT64) { + nm_sprintf_buf (numstr, "%" G_GUINT64_FORMAT, g_value_get_uint64 (&value)); nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); } else if (type == G_TYPE_INT64) { - char numstr[30]; - - nm_sprintf_buf (numstr, "%" G_GINT64_FORMAT, g_value_get_int64 (value)); + nm_sprintf_buf (numstr, "%" G_GINT64_FORMAT, g_value_get_int64 (&value)); nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); } else if (type == G_TYPE_BOOLEAN) { - nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, + g_value_get_boolean (&value) + ? "true" + : "false"); } else if (type == G_TYPE_CHAR) { - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value)); + nm_sprintf_buf (numstr, "%d", (int) g_value_get_schar (&value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); } else if (type == G_TYPE_BYTES) { GBytes *bytes; const guint8 *data; gsize len = 0; - bytes = g_value_get_boxed (value); + bytes = g_value_get_boxed (&value); data = bytes ? g_bytes_get_data (bytes, &len) : NULL; if (data != NULL && len > 0) @@ -3077,19 +3181,23 @@ write_setting_value (NMSetting *setting, } else if (type == G_TYPE_STRV) { char **array; - array = (char **) g_value_get_boxed (value); + array = (char **) g_value_get_boxed (&value); nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const char **const) array, g_strv_length (array)); } else if (type == G_TYPE_HASH_TABLE) { - write_hash_of_string (info->keyfile, setting, key, value); + write_hash_of_string (info->keyfile, setting, key, &value); } else if (type == G_TYPE_ARRAY) { - write_array_of_uint (info->keyfile, setting, key, value); - } else if (G_VALUE_HOLDS_FLAGS (value)) { - /* Flags are guint but GKeyFile has no uint reader, just uint64 */ - nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value)); - } else if (G_VALUE_HOLDS_ENUM (value)) - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_enum (value)); - else - g_warn_if_reached (); + write_array_of_uint (info->keyfile, setting, key, &value); + } else if (G_VALUE_HOLDS_FLAGS (&value)) { + nm_sprintf_buf (numstr, "%u", g_value_get_flags (&value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); + } else if (G_VALUE_HOLDS_ENUM (&value)) { + nm_sprintf_buf (numstr, "%d", g_value_get_enum (&value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); + } else + g_return_if_reached (); + +out_unset_value: + g_value_unset (&value); } GKeyFile * @@ -3101,7 +3209,7 @@ nm_keyfile_write (NMConnection *connection, gs_unref_keyfile GKeyFile *keyfile = NULL; KeyfileWriterInfo info; gs_free NMSetting **settings = NULL; - guint i, n_settings = 0; + guint i, j, n_settings = 0; g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (!error || !*error, NULL); @@ -3159,18 +3267,20 @@ nm_keyfile_write (NMConnection *connection, } } } - goto next; } - nm_setting_enumerate_values (setting, write_setting_value, &info); + for (j = 0; j < sett_info->property_infos_len; j++) { + const NMSettInfoProperty *property_info = _nm_sett_info_property_info_get_sorted (sett_info, j); -next: - if (info.error) - goto out_with_info_error; + write_setting_value (&info, setting, property_info); + if (info.error) + goto out_with_info_error; + } + + nm_assert (!info.error); } - if (info.error) - goto out_with_info_error; + nm_assert (!info.error); return g_steal_pointer (&keyfile); diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c index 36765484eb..adc8243403 100644 --- a/libnm-core/nm-setting-ip4-config.c +++ b/libnm-core/nm-setting-ip4-config.c @@ -376,9 +376,11 @@ ip4_addresses_set (NMSetting *setting, } static GVariant * -ip4_address_labels_get (NMSetting *setting, +ip4_address_labels_get (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting); gboolean have_labels = FALSE; @@ -386,6 +388,9 @@ ip4_address_labels_get (NMSetting *setting, GVariant *ret; int num_addrs, i; + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) + return NULL; + num_addrs = nm_setting_ip_config_get_num_addresses (s_ip); for (i = 0; i < num_addrs; i++) { NMIPAddress *addr = nm_setting_ip_config_get_address (s_ip, i); @@ -414,18 +419,19 @@ ip4_address_labels_get (NMSetting *setting, } static GVariant * -ip4_address_data_get (NMSetting *setting, +ip4_address_data_get (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { - GPtrArray *addrs; - GVariant *ret; + gs_unref_ptrarray GPtrArray *addrs = NULL; - g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL); - ret = nm_utils_ip_addresses_to_variant (addrs); - g_ptr_array_unref (addrs); + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) + return NULL; - return ret; + g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL); + return nm_utils_ip_addresses_to_variant (addrs); } static gboolean @@ -486,18 +492,19 @@ ip4_routes_set (NMSetting *setting, } static GVariant * -ip4_route_data_get (NMSetting *setting, +ip4_route_data_get (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { - GPtrArray *routes; - GVariant *ret; + gs_unref_ptrarray GPtrArray *routes = NULL; - g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL); - ret = nm_utils_ip_routes_to_variant (routes); - g_ptr_array_unref (routes); + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) + return NULL; - return ret; + g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL); + return nm_utils_ip_routes_to_variant (routes); } static gboolean diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c index fa65be910a..153df54f2f 100644 --- a/libnm-core/nm-setting-ip6-config.c +++ b/libnm-core/nm-setting-ip6-config.c @@ -374,18 +374,19 @@ ip6_addresses_set (NMSetting *setting, } static GVariant * -ip6_address_data_get (NMSetting *setting, +ip6_address_data_get (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { - GPtrArray *addrs; - GVariant *ret; + gs_unref_ptrarray GPtrArray *addrs = NULL; - g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL); - ret = nm_utils_ip_addresses_to_variant (addrs); - g_ptr_array_unref (addrs); + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) + return NULL; - return ret; + g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL); + return nm_utils_ip_addresses_to_variant (addrs); } static gboolean @@ -446,18 +447,19 @@ ip6_routes_set (NMSetting *setting, } static GVariant * -ip6_route_data_get (NMSetting *setting, +ip6_route_data_get (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { - GPtrArray *routes; - GVariant *ret; + gs_unref_ptrarray GPtrArray *routes = NULL; - g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL); - ret = nm_utils_ip_routes_to_variant (routes); - g_ptr_array_unref (routes); + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) + return NULL; - return ret; + g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL); + return nm_utils_ip_routes_to_variant (routes); } static gboolean diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h index 1e25226ede..49d27336fa 100644 --- a/libnm-core/nm-setting-private.h +++ b/libnm-core/nm-setting-private.h @@ -82,9 +82,11 @@ gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting, #define NM_SETTING_PARAM_GENDATA_BACKED (1 << (7 + G_PARAM_USER_SHIFT)) -GVariant *_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting, +GVariant *_nm_setting_get_deprecated_virtual_interface_name (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property); + NMSetting *setting, + NMConnectionSerializationFlags flags); NMSettingVerifyResult _nm_setting_verify (NMSetting *setting, NMConnection *connection, @@ -95,6 +97,13 @@ gboolean _nm_setting_verify_secret_string (const char *str, const char *property, GError **error); +gboolean _nm_setting_aggregate (NMSetting *setting, + NMConnectionAggregateType type, + gpointer arg); +gboolean _nm_setting_vpn_aggregate (NMSettingVpn *setting, + NMConnectionAggregateType type, + gpointer arg); + gboolean _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_type); GVariant *_nm_setting_to_dbus (NMSetting *setting, @@ -107,6 +116,11 @@ NMSetting *_nm_setting_new_from_dbus (GType setting_type, NMSettingParseFlags parse_flags, GError **error); +gboolean _nm_setting_property_is_regular_secret (NMSetting *setting, + const char *secret_name); +gboolean _nm_setting_property_is_regular_secret_flags (NMSetting *setting, + const char *secret_flags_name); + /*****************************************************************************/ static inline GArray * diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c index fcbeec6854..d485b2a802 100644 --- a/libnm-core/nm-setting-vpn.c +++ b/libnm-core/nm-setting-vpn.c @@ -461,6 +461,66 @@ nm_setting_vpn_foreach_secret (NMSettingVpn *setting, foreach_item_helper (setting, TRUE, func, user_data); } +gboolean +_nm_setting_vpn_aggregate (NMSettingVpn *setting, + NMConnectionAggregateType type, + gpointer arg) +{ + NMSettingVpnPrivate *priv; + NMSettingSecretFlags secret_flags; + const char *key_name; + GHashTableIter iter; + + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE); + + priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + switch (type) { + + case NM_CONNECTION_AGGREGATE_ANY_SECRETS: + if (g_hash_table_size (priv->secrets) > 0) { + *((gboolean *) arg) = TRUE; + return TRUE; + } + return FALSE; + + case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS: + + g_hash_table_iter_init (&iter, priv->secrets); + while (g_hash_table_iter_next (&iter, (gpointer *) &key_name, NULL)) { + if (!nm_setting_get_secret_flags (NM_SETTING (setting), key_name, &secret_flags, NULL)) + nm_assert_not_reached (); + if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) { + *((gboolean *) arg) = TRUE; + return TRUE; + } + } + + /* Ok, we have no secrets with system-secret flags. + * But do we have any secret-flags (without secrets) that indicate system secrets? */ + g_hash_table_iter_init (&iter, priv->data); + while (g_hash_table_iter_next (&iter, (gpointer *) &key_name, NULL)) { + gs_free char *secret_name = NULL; + + if (!g_str_has_suffix (key_name, "-flags")) + continue; + secret_name = g_strndup (key_name, strlen (key_name) - NM_STRLEN ("-flags")); + if (secret_name[0] == '\0') + continue; + if (!nm_setting_get_secret_flags (NM_SETTING (setting), secret_name, &secret_flags, NULL)) + nm_assert_not_reached (); + if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) { + *((gboolean *) arg) = TRUE; + return TRUE; + } + } + + return FALSE; + } + + g_return_val_if_reached (FALSE); +} + /** * nm_setting_vpn_get_timeout: * @setting: the #NMSettingVpn @@ -642,41 +702,52 @@ update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError static gboolean get_secret_flags (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags *out_flags, GError **error) { NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); - gs_free char *flags_key = NULL; - gpointer val; - unsigned long tmp; - NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; - - flags_key = g_strdup_printf ("%s-flags", secret_name); - if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) { - errno = 0; - tmp = strtoul ((const char *) val, NULL, 10); - if ((errno != 0) || (tmp > NM_SETTING_SECRET_FLAGS_ALL)) { - g_set_error (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("failed to convert value '%s' to uint"), - (const char *) val); + gs_free char *flags_key_free = NULL; + const char *flags_key; + const char *flags_val; + gint64 i64; + + flags_key = nm_construct_name_a ("%s-flags", secret_name, &flags_key_free); + + if (!g_hash_table_lookup_extended (priv->data, flags_key, NULL, (gpointer *) &flags_val)) { + NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE); + + /* having no secret flag for the secret is fine, as long as there + * is the secret itself... */ + if (!g_hash_table_contains (priv->secrets, secret_name)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET, + _("secret flags property not found")); g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, flags_key); return FALSE; } - flags = (NMSettingSecretFlags) tmp; + return TRUE; + } + + i64 = _nm_utils_ascii_str_to_int64 (flags_val, 10, 0, NM_SETTING_SECRET_FLAGS_ALL, -1); + if ( i64 == -1 + || !_nm_setting_secret_flags_valid (i64)) { + /* The flags keys is set to an unexpected value. That is a configuration + * error. Note that keys named "*-flags" are reserved for secrets. The user + * must not use this for anything but secret flags. Hence, we cannot fail + * to read the secret, we pretend that the secret flag is set to the default + * NM_SETTING_SECRET_FLAG_NONE. */ + NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE); + return TRUE; } - if (out_flags) - *out_flags = flags; + NM_SET_OUT (out_flags, (NMSettingSecretFlags) i64); return TRUE; } static gboolean set_secret_flags (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags flags, GError **error) { diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c index 38f4519b77..36a00b7c50 100644 --- a/libnm-core/nm-setting-wireless-security.c +++ b/libnm-core/nm-setting-wireless-security.c @@ -1177,53 +1177,56 @@ verify_secrets (NMSetting *setting, NMConnection *connection, GError **error) static gboolean get_secret_flags (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags *out_flags, GError **error) { - NMSettingClass *setting_class; - gboolean verify_override = verify_secret; - - /* There's only one 'flags' property for WEP keys, so alias all the WEP key - * property names to that flags property. - */ - if ( !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0) - || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1) - || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2) - || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) { - secret_name = "wep-key"; - verify_override = FALSE; /* Already know it's a secret */ + NMSettingSecretFlags flags; + + if (NM_IN_STRSET (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) { + /* There's only one 'flags' property for WEP keys, so alias all the WEP key + * property names to that flags property. */ + nm_assert (_nm_setting_property_is_regular_secret (setting, secret_name)); + nm_assert (_nm_setting_property_is_regular_secret_flags (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS)); + + g_object_get (G_OBJECT (setting), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, + &flags, + NULL); + NM_SET_OUT (out_flags, flags); + return TRUE; } - /* Chain up to superclass with modified key name */ - setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class); - return setting_class->get_secret_flags (setting, secret_name, verify_override, out_flags, error); + return NM_SETTING_CLASS (nm_setting_wireless_security_parent_class)->get_secret_flags (setting, secret_name, out_flags, error); } static gboolean set_secret_flags (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags flags, GError **error) { - NMSettingClass *setting_class; - gboolean verify_override = verify_secret; - - /* There's only one 'flags' property for WEP keys, so alias all the WEP key - * property names to that flags property. - */ - if ( !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0) - || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1) - || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2) - || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) { - secret_name = "wep-key"; - verify_override = FALSE; /* Already know it's a secret */ + if (NM_IN_STRSET (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) { + /* There's only one 'flags' property for WEP keys, so alias all the WEP key + * property names to that flags property. */ + nm_assert (_nm_setting_property_is_regular_secret (setting, secret_name)); + nm_assert (_nm_setting_property_is_regular_secret_flags (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS)); + + if (!nm_g_object_set_property_flags (G_OBJECT (setting), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, + NM_TYPE_SETTING_SECRET_FLAGS, + flags, + error)) + g_return_val_if_reached (FALSE); + return TRUE; } - /* Chain up to superclass with modified key name */ - setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class); - return setting_class->set_secret_flags (setting, secret_name, verify_override, flags, error); + return NM_SETTING_CLASS (nm_setting_wireless_security_parent_class)->set_secret_flags (setting, secret_name, flags, error); } static void diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c index 287c27dacb..02ff534b2f 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -949,14 +949,22 @@ compare_property (NMSetting *setting, /*****************************************************************************/ static GVariant * -nm_setting_wireless_get_security (NMSetting *setting, +nm_setting_wireless_get_security (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property_name) + NMSetting *setting, + NMConnectionSerializationFlags flags) { - if (nm_connection_get_setting_wireless_security (connection)) - return g_variant_new_string (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); - else + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) return NULL; + + if (!connection) + return NULL; + + if (!nm_connection_get_setting_wireless_security (connection)) + return NULL; + + return g_variant_new_string (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); } /** diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c index 4324f0a4dc..4662912a89 100644 --- a/libnm-core/nm-setting.c +++ b/libnm-core/nm-setting.c @@ -354,6 +354,77 @@ _properties_override_add_transform (GArray *properties_override, static NMSettInfoSetting _sett_info_settings[_NM_META_SETTING_TYPE_NUM]; +static int +_property_infos_sort_cmp_setting_connection (gconstpointer p_a, + gconstpointer p_b, + gpointer user_data) +{ + const NMSettInfoProperty *a = *((const NMSettInfoProperty *const*) p_a); + const NMSettInfoProperty *b = *((const NMSettInfoProperty *const*) p_b); + int c_name; + + c_name = strcmp (a->name, b->name); + nm_assert (c_name != 0); + +#define CMP_AND_RETURN(n_a, n_b, name) \ + G_STMT_START { \ + gboolean _is = nm_streq (n_a, ""name); \ + \ + if ( _is \ + || nm_streq (n_b, ""name)) \ + return _is ? -1 : 1; \ + } G_STMT_END + + /* for [connection], report first id, uuid, type in that order. */ + if (c_name != 0) { + CMP_AND_RETURN (a->name, b->name, NM_SETTING_CONNECTION_ID); + CMP_AND_RETURN (a->name, b->name, NM_SETTING_CONNECTION_UUID); + CMP_AND_RETURN (a->name, b->name, NM_SETTING_CONNECTION_TYPE); + } + +#undef CMP_AND_RETURN + + return c_name; +} + +static const NMSettInfoProperty *const* +_property_infos_sort (const NMSettInfoProperty *property_infos, + guint property_infos_len, + NMSettingClass *setting_class) +{ + const NMSettInfoProperty **arr; + guint i; + +#if NM_MORE_ASSERTS > 5 + /* assert that the property names are all unique and sorted. */ + for (i = 0; i < property_infos_len; i++) { + if (property_infos[i].param_spec) + nm_assert (nm_streq (property_infos[i].name, property_infos[i].param_spec->name)); + if (i > 0) + nm_assert (strcmp (property_infos[i - 1].name, property_infos[i].name) < 0); + } +#endif + + if (property_infos_len <= 1) + return NULL; + if (G_TYPE_FROM_CLASS (setting_class) != NM_TYPE_SETTING_CONNECTION) { + /* we only do something special for certain setting types. This one, + * has just alphabetical sorting. */ + return NULL; + } + + arr = g_new (const NMSettInfoProperty *, property_infos_len); + for (i = 0; i < property_infos_len; i++) + arr[i] = &property_infos[i]; + + g_qsort_with_data (arr, + property_infos_len, + sizeof (const NMSettInfoProperty *), + _property_infos_sort_cmp_setting_connection, + NULL); + return arr; +} + void _nm_setting_class_commit_full (NMSettingClass *setting_class, NMMetaSettingType meta_type, @@ -387,19 +458,21 @@ _nm_setting_class_commit_full (NMSettingClass *setting_class, #if NM_MORE_ASSERTS > 10 /* assert that properties_override is constructed consistently. */ for (i = 0; i < override_len; i++) { - guint j; const NMSettInfoProperty *p = &g_array_index (properties_override, NMSettInfoProperty, i); + gboolean found = FALSE; + guint j; nm_assert (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data, i, p->name)); for (j = 0; j < n_property_specs; j++) { - if (nm_streq (property_specs[j]->name, p->name)) { - nm_assert (p->param_spec == property_specs[j]); - break; - } + if (!nm_streq (property_specs[j]->name, p->name)) + continue; + nm_assert (!found); + found = TRUE; + nm_assert (p->param_spec == property_specs[j]); } - nm_assert ((j == n_property_specs) == (p->param_spec == NULL)); + nm_assert (found == (p->param_spec != NULL)); } #endif @@ -429,6 +502,10 @@ _nm_setting_class_commit_full (NMSettingClass *setting_class, sett_info->property_infos_len = properties_override->len; sett_info->property_infos = (const NMSettInfoProperty *) g_array_free (properties_override, properties_override->len == 0); + + sett_info->property_infos_sorted = _property_infos_sort (sett_info->property_infos, + sett_info->property_infos_len, + setting_class); } const NMSettInfoSetting * @@ -662,10 +739,13 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionS if (!prop_spec) { if (!property->synth_func) continue; - - if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) - continue; } else { + + /* For the moment, properties backed by a GObject property don't + * define a synth function. There is no problem supporting that, + * however, for now just disallow it. */ + nm_assert (!property->synth_func); + if (!(prop_spec->flags & G_PARAM_WRITABLE)) continue; @@ -685,14 +765,10 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionS continue; } - if (property->synth_func) { - if (!(flags & NM_CONNECTION_SERIALIZE_NO_SYNTH)) - dbus_value = property->synth_func (setting, connection, property->name); - else - dbus_value = NULL; - } else { + if (property->synth_func) + dbus_value = property->synth_func (sett_info, i, connection, setting, flags); + else dbus_value = get_property_for_dbus (setting, property, TRUE); - } if (dbus_value) { /* Allow dbus_value to be either floating or not. */ @@ -975,14 +1051,19 @@ _nm_setting_get_property (NMSetting *setting, const char *property_name, GValue } static void -duplicate_setting (NMSetting *setting, - const char *name, - const GValue *value, - GParamFlags flags, - gpointer user_data) +_gobject_copy_property (GObject *src, + GObject *dst, + const char *property_name, + GType gtype) { - if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE) - g_object_set_property (G_OBJECT (user_data), name, value); + nm_auto_unset_gvalue GValue value = G_VALUE_INIT; + + nm_assert (G_IS_OBJECT (src)); + nm_assert (G_IS_OBJECT (dst)); + + g_value_init (&value, gtype); + g_object_get_property (src, property_name, &value); + g_object_set_property (dst, property_name, &value); } /** @@ -1023,11 +1104,35 @@ nm_setting_duplicate (NMSetting *setting) g_variant_ref (val)); } } - } else { - g_object_freeze_notify (dup); - nm_setting_enumerate_values (setting, duplicate_setting, dup); - g_object_thaw_notify (dup); } + + if (sett_info->property_infos_len > 0) { + gboolean frozen = FALSE; + guint i; + + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = sett_info->property_infos[i].param_spec; + + if (!prop_spec) + continue; + if ((prop_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE) + continue; + + if (!frozen) { + g_object_freeze_notify (dup); + frozen = TRUE; + } + + _gobject_copy_property (G_OBJECT (setting), + dup, + prop_spec->name, + G_PARAM_SPEC_VALUE_TYPE (prop_spec)); + } + + if (frozen) + g_object_thaw_notify (dup); + } + return NM_SETTING (dup); } @@ -1208,9 +1313,6 @@ nm_setting_compare (NMSetting *a, NMSettingCompareFlags flags) { const NMSettInfoSetting *sett_info; - GParamSpec **property_specs; - guint n_property_specs; - int same = TRUE; guint i; g_return_val_if_fail (NM_IS_SETTING (a), FALSE); @@ -1233,9 +1335,11 @@ nm_setting_compare (NMSetting *a, } /* 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++) { - GParamSpec *prop_spec = property_specs[i]; + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = sett_info->property_infos[i].param_spec; + + if (!prop_spec) + continue; /* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */ if ( NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_FUZZY) @@ -1254,11 +1358,11 @@ nm_setting_compare (NMSetting *a, && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET)) continue; - same = NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags); + if (!NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags)) + return FALSE; } - g_free (property_specs); - return same; + return TRUE; } static inline gboolean @@ -1447,15 +1551,13 @@ nm_setting_diff (NMSetting *a, } } } else { - gs_free GParamSpec **property_specs = NULL; - guint n_property_specs; - - property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); - - for (i = 0; i < n_property_specs; i++) { - GParamSpec *prop_spec = property_specs[i]; + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = sett_info->property_infos[i].param_spec; NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN; + if (!prop_spec) + continue; + /* Handle compare flags */ if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags)) continue; @@ -1540,33 +1642,6 @@ nm_setting_diff (NMSetting *a, } } -#define CMP_AND_RETURN(n_a, n_b, name) \ - G_STMT_START { \ - gboolean _is = (strcmp (n_a, ""name) == 0); \ - \ - if (_is || (strcmp (n_b, ""name) == 0)) \ - return _is ? -1 : 1; \ - } G_STMT_END - -static int -_enumerate_values_sort (GParamSpec **p_a, GParamSpec **p_b, GType *p_type) -{ - const char *n_a = (*p_a)->name; - const char *n_b = (*p_b)->name; - int c = strcmp (n_a, n_b); - - if (c) { - if (*p_type == NM_TYPE_SETTING_CONNECTION) { - /* for [connection], report first id, uuid, type in that order. */ - CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_ID); - CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_UUID); - CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_TYPE); - } - } - return c; -} -#undef CMP_AND_RETURN - /** * nm_setting_enumerate_values: * @setting: the #NMSetting @@ -1582,10 +1657,7 @@ nm_setting_enumerate_values (NMSetting *setting, gpointer user_data) { const NMSettInfoSetting *sett_info; - GParamSpec **property_specs; - guint n_properties; guint i; - GType type; g_return_if_fail (NM_IS_SETTING (setting)); g_return_if_fail (func != NULL); @@ -1594,6 +1666,7 @@ nm_setting_enumerate_values (NMSetting *setting, if (sett_info->detail.gendata_info) { const char *const*names; + guint n_properties; /* the properties of this setting are not real GObject properties. * Hence, this API makes little sense (or does it?). Still, call @@ -1623,25 +1696,87 @@ nm_setting_enumerate_values (NMSetting *setting, 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_properties, sizeof (gpointer), - (GCompareDataFunc) _enumerate_values_sort, &type); - - for (i = 0; i < n_properties; i++) { - GParamSpec *prop_spec = property_specs[i]; + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = _nm_sett_info_property_info_get_sorted (sett_info, i)->param_spec; GValue value = G_VALUE_INIT; + if (!prop_spec) + continue; + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec)); g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); func (setting, prop_spec->name, &value, prop_spec->flags, user_data); g_value_unset (&value); } +} - g_free (property_specs); +/** + * _nm_setting_aggregate: + * @setting: the #NMSetting to aggregate. + * @type: the #NMConnectionAggregateType aggregate type. + * @arg: the in/out arguments for aggregation. They depend on @type. + * + * This is the implementation detail of _nm_connection_aggregate(). It + * makes no sense to call this function directly outside of _nm_connection_aggregate(). + * + * Returns: %TRUE if afterwards the aggregation is complete. That means, + * the only caller _nm_connection_aggregate() will not visit other settings + * after a setting returns %TRUE (indicating that there is nothing further + * to aggregate). Note that is very different from the boolean return + * argument of _nm_connection_aggregate(), which serves a different purpose. + */ +gboolean +_nm_setting_aggregate (NMSetting *setting, + NMConnectionAggregateType type, + gpointer arg) +{ + const NMSettInfoSetting *sett_info; + guint i; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (arg, FALSE); + g_return_val_if_fail (NM_IN_SET (type, NM_CONNECTION_AGGREGATE_ANY_SECRETS, + NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS), + FALSE); + + if (NM_IS_SETTING_VPN (setting)) + return _nm_setting_vpn_aggregate (NM_SETTING_VPN (setting), type, arg); + + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = sett_info->property_infos[i].param_spec; + nm_auto_unset_gvalue GValue value = G_VALUE_INIT; + NMSettingSecretFlags secret_flags; + + if (!prop_spec) + continue; + if (!NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET)) + continue; + + switch (type) { + + case NM_CONNECTION_AGGREGATE_ANY_SECRETS: + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec)); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); + if (!g_param_value_defaults (prop_spec, &value)) { + *((gboolean *) arg) = TRUE; + return TRUE; + } + break; + + case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS: + if (!nm_setting_get_secret_flags (setting, prop_spec->name, &secret_flags, NULL)) + nm_assert_not_reached (); + if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) { + *((gboolean *) arg) = TRUE; + return TRUE; + } + break; + + } + } + + return FALSE; } /** @@ -1657,16 +1792,18 @@ nm_setting_enumerate_values (NMSetting *setting, gboolean _nm_setting_clear_secrets (NMSetting *setting) { - gs_free GParamSpec **property_specs = NULL; - guint n_property_specs; - guint i; + const NMSettInfoSetting *sett_info; gboolean changed = FALSE; + guint i; 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]; + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = sett_info->property_infos[i].param_spec; + + if (!prop_spec) + continue; if (prop_spec->flags & NM_SETTING_PARAM_SECRET) { GValue value = G_VALUE_INIT; @@ -1731,23 +1868,27 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting, NMSettingClearSecretsWithFlagsFn func, gpointer user_data) { - gs_free GParamSpec **property_specs = NULL; - guint n_property_specs; - guint i; + const NMSettInfoSetting *sett_info; gboolean changed = FALSE; + guint i; - 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) { - changed |= NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting, - property_specs[i], - func, - user_data); - } + sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting)); + for (i = 0; i < sett_info->property_infos_len; i++) { + GParamSpec *prop_spec = sett_info->property_infos[i].param_spec; + + if (!prop_spec) + continue; + + if (!NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET)) + continue; + + changed |= NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting, + prop_spec, + func, + user_data); } return changed; } @@ -1872,52 +2013,72 @@ _nm_setting_update_secrets (NMSetting *setting, GVariant *secrets, GError **erro return result; } -static gboolean -is_secret_prop (NMSetting *setting, const char *secret_name, GError **error) +static void +_set_error_secret_property_not_found (GError **error, + NMSetting *setting, + const char *secret_name) +{ + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_PROPERTY_NOT_FOUND, + _("not a secret property")); + g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), secret_name); +} + +gboolean +_nm_setting_property_is_regular_secret (NMSetting *setting, + const char *secret_name) { const NMSettInfoProperty *property; - GParamSpec *pspec; + + nm_assert (NM_IS_SETTING (setting)); + nm_assert (secret_name); property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), secret_name); - if (!property) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_PROPERTY_NOT_FOUND, - _("secret is not set")); - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), secret_name); - return FALSE; - } + return property + && property->param_spec + && NM_FLAGS_HAS (property->param_spec->flags, NM_SETTING_PARAM_SECRET); +} - pspec = property->param_spec; - if (!pspec || !(pspec->flags & NM_SETTING_PARAM_SECRET)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET, - _("not a secret property")); - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), secret_name); - return FALSE; - } +gboolean +_nm_setting_property_is_regular_secret_flags (NMSetting *setting, + const char *secret_flags_name) +{ + const NMSettInfoProperty *property; - return TRUE; + nm_assert (NM_IS_SETTING (setting)); + nm_assert (secret_flags_name); + + property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), secret_flags_name); + return property + && property->param_spec + && !NM_FLAGS_HAS (property->param_spec->flags, NM_SETTING_PARAM_SECRET) + && G_PARAM_SPEC_VALUE_TYPE (property->param_spec) == NM_TYPE_SETTING_SECRET_FLAGS; } static gboolean get_secret_flags (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags *out_flags, GError **error) { - gs_free char *name_to_free = NULL; - NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + gs_free char *secret_flags_name_free = NULL; + const char *secret_flags_name; + NMSettingSecretFlags flags; - if (verify_secret && !is_secret_prop (setting, secret_name, error)) { + if (!_nm_setting_property_is_regular_secret (setting, + secret_name)) { + _set_error_secret_property_not_found (error, setting, secret_name); NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE); return FALSE; } + secret_flags_name = nm_construct_name_a ("%s-flags", secret_name, &secret_flags_name_free); + + nm_assert (_nm_setting_property_is_regular_secret_flags (setting, secret_flags_name)); + g_object_get (G_OBJECT (setting), - nm_construct_name_a ("%s-flags", secret_name, &name_to_free), + secret_flags_name, &flags, NULL); NM_SET_OUT (out_flags, flags); @@ -1946,25 +2107,34 @@ nm_setting_get_secret_flags (NMSetting *setting, g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); g_return_val_if_fail (secret_name != NULL, FALSE); - return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, TRUE, out_flags, error); + return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, out_flags, error); } static gboolean set_secret_flags (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags flags, GError **error) { - gs_free char *name_to_free = NULL; + gs_free char *secret_flags_name_free = NULL; + const char *secret_flags_name; + + if (!_nm_setting_property_is_regular_secret (setting, + secret_name)) { + _set_error_secret_property_not_found (error, setting, secret_name); + return FALSE; + } - if (verify_secret) - g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE); + secret_flags_name = nm_construct_name_a ("%s-flags", secret_name, &secret_flags_name_free); - g_object_set (G_OBJECT (setting), - nm_construct_name_a ("%s-flags", secret_name, &name_to_free), - flags, - NULL); + nm_assert (_nm_setting_property_is_regular_secret_flags (setting, secret_flags_name)); + + if (!nm_g_object_set_property_flags (G_OBJECT (setting), + secret_flags_name, + NM_TYPE_SETTING_SECRET_FLAGS, + flags, + error)) + g_return_val_if_reached (FALSE); return TRUE; } @@ -1989,9 +2159,9 @@ nm_setting_set_secret_flags (NMSetting *setting, { 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); + g_return_val_if_fail (_nm_setting_secret_flags_valid (flags), FALSE); - return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, TRUE, flags, error); + return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, flags, error); } /** @@ -2018,8 +2188,7 @@ nm_setting_to_string (NMSetting *setting) string = g_string_new (nm_setting_get_name (setting)); g_string_append_c (string, '\n'); - variant = _nm_setting_to_dbus (setting, NULL, NM_CONNECTION_SERIALIZE_ALL - | NM_CONNECTION_SERIALIZE_NO_SYNTH); + variant = _nm_setting_to_dbus (setting, NULL, NM_CONNECTION_SERIALIZE_ALL); g_variant_iter_init (&iter, variant); while ((child = g_variant_iter_next_value (&iter))) { @@ -2037,9 +2206,11 @@ nm_setting_to_string (NMSetting *setting) } GVariant * -_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting, +_nm_setting_get_deprecated_virtual_interface_name (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { NMSettingConnection *s_con; diff --git a/libnm-core/nm-setting.h b/libnm-core/nm-setting.h index d6876f86d2..e3c128ffb2 100644 --- a/libnm-core/nm-setting.h +++ b/libnm-core/nm-setting.h @@ -191,13 +191,11 @@ typedef struct { gboolean (*get_secret_flags) (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags *out_flags, GError **error); gboolean (*set_secret_flags) (NMSetting *setting, const char *secret_name, - gboolean verify_secret, NMSettingSecretFlags flags, GError **error); diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index b886730a35..58822cdeff 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -56,9 +56,11 @@ gboolean _nm_utils_hwaddr_cloned_not_set (NMSetting *setting, const char *property, NMSettingParseFlags parse_flags, GError **error); -GVariant * _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, +GVariant * _nm_utils_hwaddr_cloned_data_synth (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property); + NMSetting *setting, + NMConnectionSerializationFlags flags); gboolean _nm_utils_hwaddr_cloned_data_set (NMSetting *setting, GVariant *connection_dict, const char *property, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 69657c9dce..4709971281 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -4234,13 +4234,18 @@ _nm_utils_hwaddr_cloned_not_set (NMSetting *setting, } GVariant * -_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, +_nm_utils_hwaddr_cloned_data_synth (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { gs_free char *addr = NULL; - nm_assert (nm_streq0 (property, "assigned-mac-address")); + if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) + return NULL; + + nm_assert (nm_streq0 (sett_info->property_infos[property_idx].name, "assigned-mac-address")); g_object_get (setting, "cloned-mac-address", @@ -4261,7 +4266,9 @@ _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, * To preserve that behavior, serialize "" as NULL. */ - return addr && addr[0] ? g_variant_new_string (addr) : NULL; + return addr && addr[0] + ? g_variant_new_take_string (g_steal_pointer (&addr)) + : NULL; } gboolean diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 91f116de43..b6dd532616 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -755,10 +755,12 @@ vpn_check_empty_func (const char *key, const char *value, gpointer user_data) static void test_setting_vpn_items (void) { - gs_unref_object NMSettingVpn *s_vpn = NULL; + gs_unref_object NMConnection *connection = NULL; + NMSettingVpn *s_vpn; - s_vpn = (NMSettingVpn *) nm_setting_vpn_new (); - g_assert (s_vpn); + connection = nmtst_create_minimal_connection ("vpn-items", NULL, NM_SETTING_VPN_SETTING_NAME, NULL); + + s_vpn = nm_connection_get_setting_vpn (connection); nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1"); nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2"); @@ -772,7 +774,14 @@ test_setting_vpn_items (void) nm_setting_vpn_remove_data_item (s_vpn, "foobar3"); nm_setting_vpn_remove_data_item (s_vpn, "foobar4"); + g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1"); + + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2"); nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3"); nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4"); @@ -782,8 +791,25 @@ test_setting_vpn_items (void) nm_setting_vpn_remove_secret (s_vpn, "foobar1"); nm_setting_vpn_remove_secret (s_vpn, "foobar2"); nm_setting_vpn_remove_secret (s_vpn, "foobar3"); + + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + + nm_setting_vpn_add_data_item (s_vpn, "foobar4-flags", "blahblah4"); + + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + + nm_setting_vpn_add_data_item (s_vpn, "foobar4-flags", "2"); + + g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + nm_setting_vpn_remove_secret (s_vpn, "foobar4"); + g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + + nm_setting_vpn_remove_data_item (s_vpn, "foobar4-flags"); + /* Try to add some blank values and make sure they are rejected */ NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL)); nm_setting_vpn_add_data_item (s_vpn, NULL, NULL); @@ -1632,6 +1658,25 @@ test_connection_to_dbus_setting_name (void) s_wsec = make_test_wsec_setting ("connection-to-dbus-setting-name"); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED, + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED, + NULL); + + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NONE, + NULL); + + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)); + g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL)); + dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); /* Make sure the keys of the first level dict are setting names, not diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 79c8257610..66842db92d 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -45,6 +45,77 @@ /*****************************************************************************/ +/* converts @dict to a connection. In this case, @dict must be good, without warnings, so that + * NM_SETTING_PARSE_FLAGS_STRICT and NM_SETTING_PARSE_FLAGS_BEST_EFFORT yield the exact same results. */ +static NMConnection * +_connection_new_from_dbus_strict (GVariant *dict, + gboolean normalize) +{ + gs_unref_object NMConnection *con_x_0 = NULL; + gs_unref_object NMConnection *con_x_s = NULL; + gs_unref_object NMConnection *con_x_e = NULL; + gs_unref_object NMConnection *con_n_0 = NULL; + gs_unref_object NMConnection *con_n_s = NULL; + gs_unref_object NMConnection *con_n_e = NULL; + gs_free_error GError *error = NULL; + guint i; + + g_assert (g_variant_is_of_type (dict, NM_VARIANT_TYPE_CONNECTION)); + + con_x_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NONE, &error); + nmtst_assert_success (NM_IS_CONNECTION (con_x_0), error); + + con_x_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT, &error); + nmtst_assert_success (NM_IS_CONNECTION (con_x_s), error); + + con_x_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + nmtst_assert_success (NM_IS_CONNECTION (con_x_e), error); + + con_n_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NORMALIZE, &error); + nmtst_assert_success (NM_IS_CONNECTION (con_n_0), error); + + con_n_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error); + nmtst_assert_success (NM_IS_CONNECTION (con_n_s), error); + + con_n_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error); + nmtst_assert_success (NM_IS_CONNECTION (con_n_e), error); + + nmtst_assert_connection_verifies (con_x_0); + nmtst_assert_connection_verifies (con_x_e); + nmtst_assert_connection_verifies (con_x_s); + + nmtst_assert_connection_verifies_without_normalization (con_n_0); + nmtst_assert_connection_verifies_without_normalization (con_n_e); + nmtst_assert_connection_verifies_without_normalization (con_n_s); + + /* randomly compare some pairs that we created. They must all be equal, + * after accounting for normalization. */ + for (i = 0; i < 10; i++) { + NMConnection *cons[] = { con_x_0, con_x_s, con_x_e, con_n_0, con_n_s, con_n_e }; + guint idx_a = (nmtst_get_rand_int () % G_N_ELEMENTS (cons)); + guint idx_b = (nmtst_get_rand_int () % G_N_ELEMENTS (cons)); + gboolean normalize_a, normalize_b; + + if (idx_a <= 2 && idx_b <= 2) { + normalize_a = nmtst_get_rand_bool (); + normalize_b = normalize_a; + } else if (idx_a > 2 && idx_b > 2) { + normalize_a = nmtst_get_rand_bool (); + normalize_b = nmtst_get_rand_bool (); + } else { + normalize_a = (idx_a <= 2) ? TRUE : nmtst_get_rand_bool (); + normalize_b = (idx_b <= 2) ? TRUE : nmtst_get_rand_bool (); + } + nmtst_assert_connection_equals (cons[idx_a], normalize_a, cons[idx_b], normalize_b); + } + + return (normalize) + ? g_steal_pointer (&con_x_0) + : g_steal_pointer (&con_n_0); +} + +/*****************************************************************************/ + static void compare_blob_data (const char *test, const char *key_path, @@ -1943,6 +2014,176 @@ test_tc_config_dbus (void) /*****************************************************************************/ +static void +test_roundtrip_conversion (gconstpointer test_data) +{ + const int MODE = GPOINTER_TO_INT (test_data); + const char *ID= nm_sprintf_bufa (100, "roundtip-conversion-%d", MODE); + const char *UUID= "63376701-b61e-4318-bf7e-664a1c1eeaab"; + const char *INTERFACE_NAME = nm_sprintf_bufa (100, "ifname%d", MODE); + guint32 ETH_MTU = nmtst_rand_select ((guint32) 0u, + nmtst_get_rand_int ()); + gs_unref_ptrarray GPtrArray *kf_data_arr = g_ptr_array_new_with_free_func (g_free); + const NMConnectionSerializationFlags dbus_serialization_flags[] = { + NM_CONNECTION_SERIALIZE_ALL, + NM_CONNECTION_SERIALIZE_NO_SECRETS, + NM_CONNECTION_SERIALIZE_ONLY_SECRETS, + }; + guint dbus_serialization_flags_idx; + gs_unref_object NMConnection *con = NULL; + gs_free_error GError *error = NULL; + guint kf_data_idx; + NMSettingConnection *s_con = NULL; + NMSettingWired *s_eth = NULL; + + switch (MODE) { + case 0: + con = nmtst_create_minimal_connection (ID, UUID, NM_SETTING_WIRED_SETTING_NAME, &s_con); + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, + INTERFACE_NAME, + NULL); + nmtst_connection_normalize (con); + + s_eth = NM_SETTING_WIRED (nm_connection_get_setting (con, NM_TYPE_SETTING_WIRED)); + g_assert (NM_IS_SETTING_WIRED (s_eth)); + + g_object_set (s_eth, + NM_SETTING_WIRED_MTU, + ETH_MTU, + NULL); + + g_ptr_array_add (kf_data_arr, + g_strdup_printf ("[connection]\n" + "id=%s\n" + "uuid=%s\n" + "type=ethernet\n" + "interface-name=%s\n" + "permissions=\n" + "\n" + "[ethernet]\n" + "mac-address-blacklist=\n" + "%s" /* mtu */ + "\n" + "[ipv4]\n" + "dns-search=\n" + "method=auto\n" + "\n" + "[ipv6]\n" + "addr-gen-mode=stable-privacy\n" + "dns-search=\n" + "method=auto\n" + "", + ID, + UUID, + INTERFACE_NAME, + (ETH_MTU != 0) + ? nm_sprintf_bufa (100, "mtu=%u\n", ETH_MTU) + : "")); + + g_ptr_array_add (kf_data_arr, + g_strdup_printf ("[connection]\n" + "id=%s\n" + "uuid=%s\n" + "type=ethernet\n" + "interface-name=%s\n" + "permissions=\n" + "\n" + "[ethernet]\n" + "mac-address-blacklist=\n" + "%s" /* mtu */ + "\n" + "[ipv4]\n" + "dns-search=\n" + "method=auto\n" + "\n" + "[ipv6]\n" + "addr-gen-mode=stable-privacy\n" + "dns-search=\n" + "method=auto\n" + "", + ID, + UUID, + INTERFACE_NAME, + (ETH_MTU != 0) + ? nm_sprintf_bufa (100, "mtu=%d\n", (int) ETH_MTU) + : "")); + + break; + + default: + g_assert_not_reached (); + } + + /* the first kf_data_arr entry is special: it is the exact result of what we expect + * when converting @con to keyfile. Write @con to keyfile and compare the expected result + * literally. */ + { + gs_unref_keyfile GKeyFile *kf = NULL; + + kf = nm_keyfile_write (con, NULL, NULL, &error); + nmtst_assert_success (kf, error); + + /* the first kf_data_arr entry is special: it must be what the writer would + * produce again. */ + nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1); + } + + /* check that reading any of kf_data_arr yields the same result that we expect. */ + for (kf_data_idx = 0; kf_data_idx < kf_data_arr->len; kf_data_idx++) { + gs_unref_object NMConnection *con2 = NULL; + NMSettingWired *s_eth2 = NULL; + + con2 = nmtst_create_connection_from_keyfile (kf_data_arr->pdata[kf_data_idx], "/no/where/file.nmconnection"); + + switch (MODE) { + case 0: + s_eth2 = NM_SETTING_WIRED (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIRED)); + g_assert (NM_IS_SETTING_WIRED (s_eth2)); + + if ( ETH_MTU > (guint32) G_MAXINT + && kf_data_idx == 1) { + /* older versions wrote values > 2^21 as signed integers, but the reader would + * always reject such negative values for G_TYPE_UINT. + * + * The test case kf_data_idx #1 still writes the values in the old style. + * The behavior was fixed, but such values are still rejected as invalid. + * + * Patch the setting so that the comparison below succeeds are usual. */ + g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, 0); + g_object_set (s_eth2, + NM_SETTING_WIRED_MTU, + ETH_MTU, + NULL); + } + + g_assert_cmpint (nm_setting_wired_get_mtu (s_eth), ==, ETH_MTU); + g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, ETH_MTU); + break; + } + + nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ()); + } + + for (dbus_serialization_flags_idx = 0; dbus_serialization_flags_idx < G_N_ELEMENTS (dbus_serialization_flags); dbus_serialization_flags_idx++) { + NMConnectionSerializationFlags flag = dbus_serialization_flags[dbus_serialization_flags_idx]; + gs_unref_variant GVariant *con_var = NULL; + gs_unref_object NMConnection *con2 = NULL; + + con_var = nm_connection_to_dbus (con, flag); + g_assert (g_variant_is_of_type (con_var, NM_VARIANT_TYPE_CONNECTION)); + g_assert (g_variant_is_floating (con_var)); + g_variant_ref_sink (con_var); + + if (flag == NM_CONNECTION_SERIALIZE_ALL) { + con2 = _connection_new_from_dbus_strict (con_var, TRUE); + nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ()); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -2019,5 +2260,7 @@ main (int argc, char **argv) g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config); #endif + g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion); + return g_test_run (); } diff --git a/po/POTFILES.in b/po/POTFILES.in index 942b066131..33192b1dd0 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -56,6 +56,7 @@ libnm-core/nm-crypto-gnutls.c libnm-core/nm-crypto-nss.c libnm-core/nm-connection.c libnm-core/nm-dbus-utils.c +libnm-core/nm-keyfile-utils.c libnm-core/nm-keyfile.c libnm-core/nm-setting-6lowpan.c libnm-core/nm-setting-8021x.c diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h index 770cf0fe39..b7534edd2a 100644 --- a/shared/nm-utils/nm-glib.h +++ b/shared/nm-utils/nm-glib.h @@ -538,4 +538,28 @@ _nm_g_variant_new_printf (const char *format_string, ...) /*****************************************************************************/ +#if !GLIB_CHECK_VERSION (2, 47, 1) +/* Older versions of g_value_unset() only allowed to unset a GValue which + * was initialized previously. This was relaxed ([1], [2], [3]). + * + * Our nm_auto_unset_gvalue macro requires to be able to call g_value_unset(). + * Also, it is our general practice to allow for that. Add a compat implementation. + * + * [1] https://gitlab.gnome.org/GNOME/glib/commit/4b2d92a864f1505f1b08eb639d74293fa32681da + * [2] commit "Allow passing unset GValues to g_value_unset()" + * [3] https://bugzilla.gnome.org/show_bug.cgi?id=755766 + */ +static inline void +_nm_g_value_unset (GValue *value) +{ + g_return_if_fail (value); + + if (value->g_type != 0) + g_value_unset (value); +} +#define g_value_unset _nm_g_value_unset +#endif + +/*****************************************************************************/ + #endif /* __NM_GLIB_H__ */ diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 7cebd56cc0..3578a55291 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -829,6 +829,9 @@ fcn (void) \ #define nm_streq(s1, s2) (strcmp (s1, s2) == 0) #define nm_streq0(s1, s2) (g_strcmp0 (s1, s2) == 0) +#define NM_STR_HAS_PREFIX(str, prefix) \ + (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0) + /*****************************************************************************/ static inline GString * diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index 9b020429d6..6131d3e809 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -1108,7 +1108,7 @@ nm_utils_error_is_notfound (GError *error) */ gboolean nm_g_object_set_property (GObject *object, - const char *property_name, + const char *property_name, const GValue *value, GError **error) { @@ -1181,17 +1181,90 @@ nm_g_object_set_property (GObject *object, return TRUE; } +#define _set_property(object, property_name, gtype, gtype_set, value, error) \ + G_STMT_START { \ + nm_auto_unset_gvalue GValue gvalue = { 0 }; \ + \ + g_value_init (&gvalue, gtype); \ + gtype_set (&gvalue, (value)); \ + return nm_g_object_set_property ((object), (property_name), &gvalue, (error)); \ + } G_STMT_END + +gboolean +nm_g_object_set_property_string (GObject *object, + const char *property_name, + const char *value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_STRING, g_value_set_string, value, error); +} + +gboolean +nm_g_object_set_property_string_static (GObject *object, + const char *property_name, + const char *value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error); +} + +gboolean +nm_g_object_set_property_string_take (GObject *object, + const char *property_name, + char *value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_STRING, g_value_take_string, value, error); +} + gboolean nm_g_object_set_property_boolean (GObject *object, - const char *property_name, + const char *property_name, gboolean value, GError **error) { - nm_auto_unset_gvalue GValue gvalue = { 0 }; + _set_property (object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error); +} - g_value_init (&gvalue, G_TYPE_BOOLEAN); - g_value_set_boolean (&gvalue, !!value); - return nm_g_object_set_property (object, property_name, &gvalue, error); +gboolean +nm_g_object_set_property_char (GObject *object, + const char *property_name, + gint8 value, + GError **error) +{ + /* glib says about G_TYPE_CHAR: + * + * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer. + * + * This is always a (signed!) char. */ + _set_property (object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error); +} + +gboolean +nm_g_object_set_property_uchar (GObject *object, + const char *property_name, + guint8 value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error); +} + +gboolean +nm_g_object_set_property_int (GObject *object, + const char *property_name, + int value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_INT, g_value_set_int, value, error); +} + +gboolean +nm_g_object_set_property_int64 (GObject *object, + const char *property_name, + gint64 value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_INT64, g_value_set_int64, value, error); } gboolean @@ -1200,11 +1273,44 @@ nm_g_object_set_property_uint (GObject *object, guint value, GError **error) { - nm_auto_unset_gvalue GValue gvalue = { 0 }; + _set_property (object, property_name, G_TYPE_UINT, g_value_set_uint, value, error); +} - g_value_init (&gvalue, G_TYPE_UINT); - g_value_set_uint (&gvalue, value); - return nm_g_object_set_property (object, property_name, &gvalue, error); +gboolean +nm_g_object_set_property_uint64 (GObject *object, + const char *property_name, + guint64 value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error); +} + +gboolean +nm_g_object_set_property_flags (GObject *object, + const char *property_name, + GType gtype, + guint value, + GError **error) +{ + nm_assert (({ + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); + G_IS_FLAGS_CLASS (gtypeclass); + })); + _set_property (object, property_name, gtype, g_value_set_flags, value, error); +} + +gboolean +nm_g_object_set_property_enum (GObject *object, + const char *property_name, + GType gtype, + int value, + GError **error) +{ + nm_assert (({ + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); + G_IS_ENUM_CLASS (gtypeclass); + })); + _set_property (object, property_name, gtype, g_value_set_enum, value, error); } GParamSpec * diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index eab9f5acfc..e560697b1a 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -697,20 +697,72 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal) /*****************************************************************************/ gboolean nm_g_object_set_property (GObject *object, - const char *property_name, + const char *property_name, const GValue *value, GError **error); +gboolean nm_g_object_set_property_string (GObject *object, + const char *property_name, + const char *value, + GError **error); + +gboolean nm_g_object_set_property_string_static (GObject *object, + const char *property_name, + const char *value, + GError **error); + +gboolean nm_g_object_set_property_string_take (GObject *object, + const char *property_name, + char *value, + GError **error); + gboolean nm_g_object_set_property_boolean (GObject *object, - const char *property_name, + const char *property_name, gboolean value, GError **error); +gboolean nm_g_object_set_property_char (GObject *object, + const char *property_name, + gint8 value, + GError **error); + +gboolean nm_g_object_set_property_uchar (GObject *object, + const char *property_name, + guint8 value, + GError **error); + +gboolean nm_g_object_set_property_int (GObject *object, + const char *property_name, + int value, + GError **error); + +gboolean nm_g_object_set_property_int64 (GObject *object, + const char *property_name, + gint64 value, + GError **error); + gboolean nm_g_object_set_property_uint (GObject *object, - const char *property_name, + const char *property_name, guint value, GError **error); +gboolean nm_g_object_set_property_uint64 (GObject *object, + const char *property_name, + guint64 value, + GError **error); + +gboolean nm_g_object_set_property_flags (GObject *object, + const char *property_name, + GType gtype, + guint value, + GError **error); + +gboolean nm_g_object_set_property_enum (GObject *object, + const char *property_name, + GType gtype, + int value, + GError **error); + GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, const char *property_name); diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index 3216593e70..c235d93d50 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -905,6 +905,16 @@ nmtst_rand_buf (GRand *rand, gpointer buffer, gsize buffer_length) return buffer; } +#define _nmtst_rand_select(uniq, v0, ...) \ + ({ \ + typeof (v0) NM_UNIQ_T (UNIQ, uniq)[1 + NM_NARG (__VA_ARGS__)] = { (v0), __VA_ARGS__ }; \ + \ + NM_UNIQ_T (UNIQ, uniq)[nmtst_get_rand_int () % G_N_ELEMENTS (NM_UNIQ_T (UNIQ, uniq))]; \ + }) + +#define nmtst_rand_select(...) \ + _nmtst_rand_select (NM_UNIQ, __VA_ARGS__) + static inline void * nmtst_rand_perm (GRand *rand, void *dst, const void *src, gsize elmt_size, gsize n_elmt) { @@ -1966,8 +1976,8 @@ nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const cha static inline NMConnection * nmtst_create_connection_from_keyfile (const char *keyfile_str, const char *full_filename) { - GKeyFile *keyfile; - GError *error = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + gs_free_error GError *error = NULL; gboolean success; NMConnection *con; gs_free char *filename = g_path_get_basename (full_filename); @@ -1978,14 +1988,10 @@ nmtst_create_connection_from_keyfile (const char *keyfile_str, const char *full_ keyfile = g_key_file_new (); success = g_key_file_load_from_data (keyfile, keyfile_str, strlen (keyfile_str), G_KEY_FILE_NONE, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); con = nm_keyfile_read (keyfile, base_dir, NULL, NULL, &error); - g_assert_no_error (error); - g_assert (NM_IS_CONNECTION (con)); - - g_key_file_unref (keyfile); + nmtst_assert_success (NM_IS_CONNECTION (con), error); nm_keyfile_read_ensure_id (con, filename); nm_keyfile_read_ensure_uuid (con, full_filename); @@ -2141,4 +2147,50 @@ typedef enum { #endif /* __NM_CONNECTION_H__ */ +/*****************************************************************************/ + +static inline void +nmtst_keyfile_assert_data (GKeyFile *kf, const char *data, gssize data_len) +{ + gs_unref_keyfile GKeyFile *kf2 = NULL; + gs_free_error GError *error = NULL; + gs_free char *d1 = NULL; + gs_free char *d2 = NULL; + gboolean success; + gsize d1_len; + gsize d2_len; + + g_assert (kf); + g_assert (data || data_len == 0); + g_assert (data_len >= -1); + + d1 = g_key_file_to_data (kf, &d1_len, &error); + nmtst_assert_success (d1, error); + + if (data_len == -1) { + g_assert_cmpint (strlen (d1), ==, d1_len); + data_len = strlen (data); + g_assert_cmpstr (d1, ==, data); + } + + g_assert_cmpmem (d1, d1_len, data, (gsize) data_len); + + /* also check that we can re-generate the same keyfile from the data. */ + + kf2 = g_key_file_new (); + success = g_key_file_load_from_data (kf2, + d1, + d1_len, + G_KEY_FILE_NONE, + &error); + nmtst_assert_success (success, error); + + d2 = g_key_file_to_data (kf2, &d2_len, &error); + nmtst_assert_success (d2, error); + + g_assert_cmpmem (d2, d2_len, d1, d1_len); +} + +/*****************************************************************************/ + #endif /* __NM_TEST_UTILS_H__ */ diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 953d3ce459..edadee14bf 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -1059,49 +1059,6 @@ _con_get_request_start_validated (NMAuthChain *chain, } static void -has_system_secrets_check (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - gboolean *has_system = user_data; - - if (!(flags & NM_SETTING_PARAM_SECRET)) - return; - - /* Clear out system-owned or always-ask secrets */ - if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) { - GHashTableIter iter; - const char *secret_name = NULL; - - /* VPNs are special; need to handle each secret separately */ - g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value)); - while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) { - secret_flags = NM_SETTING_SECRET_FLAG_NONE; - nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL); - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - *has_system = TRUE; - } - } else { - if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) - g_return_if_reached (); - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - *has_system = TRUE; - } -} - -static gboolean -has_system_secrets (NMConnection *connection) -{ - gboolean has_system = FALSE; - - nm_connection_for_each_setting_value (connection, has_system_secrets_check, &has_system); - return has_system; -} - -static void _con_get_request_start (Request *req) { NMAgentManager *self; @@ -1121,7 +1078,8 @@ _con_get_request_start (Request *req) * unprivileged users. */ if ( (req->con.get.flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) - && (req->con.get.existing_secrets || has_system_secrets (req->con.connection))) { + && ( req->con.get.existing_secrets + || _nm_connection_aggregate (req->con.connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL))) { _LOGD (NULL, "("LOG_REQ_FMT") request has system secrets; checking agent %s for MODIFY", LOG_REQ_ARG (req), agent_dbus_owner); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 392823f399..0beb5ea770 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -1660,38 +1660,6 @@ typedef struct { } UpdateInfo; static void -has_some_secrets_cb (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - GParamSpec *pspec; - - if (NM_IS_SETTING_VPN (setting)) { - if (nm_setting_vpn_get_num_secrets (NM_SETTING_VPN(setting))) - *((gboolean *) user_data) = TRUE; - return; - } - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), key); - if (pspec) { - if ( (flags & NM_SETTING_PARAM_SECRET) - && !g_param_value_defaults (pspec, (GValue *)value)) - *((gboolean *) user_data) = TRUE; - } -} - -static gboolean -any_secrets_present (NMConnection *self) -{ - gboolean has_secrets = FALSE; - - nm_connection_for_each_setting_value (self, has_some_secrets_cb, &has_secrets); - return has_secrets; -} - -static void cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); @@ -1758,7 +1726,7 @@ update_auth_cb (NMSettingsConnection *self, } if (info->new_settings) { - if (!any_secrets_present (info->new_settings)) { + if (!_nm_connection_aggregate (info->new_settings, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)) { /* If the new connection has no secrets, we do not want to remove all * secrets, rather we keep all the existing ones. Do that by merging * them in to the new connection. |