diff options
| author | Michael Biebl <biebl@debian.org> | 2023-02-10 11:50:34 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2023-02-10 11:50:34 +0100 |
| commit | 1372848511cb896b80b51ed1a3e9606bd9816631 (patch) | |
| tree | 674792b9385bdef935988894b45f06b2af39f88c /src/libnm-core-impl/nm-setting.c | |
| parent | 40ec077ea305994c1fc2130add6787ca0c73e2c6 (diff) | |
New upstream version 1.42.0 upstream/1.42.0
Diffstat (limited to 'src/libnm-core-impl/nm-setting.c')
| -rw-r--r-- | src/libnm-core-impl/nm-setting.c | 397 |
1 files changed, 341 insertions, 56 deletions
diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index b6f72137..5a46a1b4 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -331,7 +331,7 @@ _nm_setting_class_commit(NMSettingClass *setting_class, override_len = properties_override->len; for (i = 0; i < override_len; i++) { - NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i); + NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); nm_assert((!!p->name) != (!!p->param_spec)); @@ -346,14 +346,14 @@ _nm_setting_class_commit(NMSettingClass *setting_class, #if NM_MORE_ASSERTS > 10 /* assert that properties_override is constructed consistently. */ for (i = 0; i < override_len; i++) { - const NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i); + const NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); gboolean found = FALSE; guint k; - nm_assert( - !_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data, - i, - p->name)); + nm_assert(!_nm_sett_info_property_find_in_array( + nm_g_array_index_p(properties_override, NMSettInfoProperty, 0), + i, + p->name)); for (k = 0; k < n_property_specs; k++) { if (!nm_streq(property_specs[k]->name, p->name)) continue; @@ -369,20 +369,20 @@ _nm_setting_class_commit(NMSettingClass *setting_class, const char *name = property_specs[i]->name; NMSettInfoProperty *p; - if (_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data, - override_len, - name)) + if (_nm_sett_info_property_find_in_array( + nm_g_array_index_p(properties_override, NMSettInfoProperty, 0), + override_len, + name)) continue; - g_array_set_size(properties_override, properties_override->len + 1); - p = &g_array_index(properties_override, NMSettInfoProperty, properties_override->len - 1); + p = nm_g_array_append_new(properties_override, NMSettInfoProperty); memset(p, 0, sizeof(*p)); p->name = name; p->param_spec = property_specs[i]; } for (i = 0; i < properties_override->len; i++) { - NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i); + NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); GType vtype; if (p->property_type) @@ -492,12 +492,12 @@ _nm_sett_info_setting_get_property_info(const NMSettInfoSetting *sett_info, return NULL; G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMSettInfoProperty, name) == 0); - idx = nm_utils_array_find_binary_search(sett_info->property_infos, - sizeof(NMSettInfoProperty), - sett_info->property_infos_len, - &property_name, - nm_strcmp_p_with_data, - NULL); + idx = nm_array_find_bsearch(sett_info->property_infos, + sett_info->property_infos_len, + sizeof(NMSettInfoProperty), + &property_name, + nm_strcmp_p_with_data, + NULL); if (idx < 0) return NULL; @@ -604,7 +604,48 @@ _nm_setting_use_legacy_property(NMSetting *setting, const char *new_property) { gs_unref_variant GVariant *setting_dict = NULL; - gs_unref_variant GVariant *value = NULL; + gs_unref_variant GVariant *val_leg = NULL; + gs_unref_variant GVariant *val_new = NULL; + + /* We want to be both forward and backward compatible (both the client or the daemon + * can be newer). + * + * For the most part, we achieve that by ignoring unknown properties (to be forward + * compatible). That of course has the downside, that we don't do strong validation + * of the input. + * + * In some cases, we deprecated a D-Bus property for another one (e.g. the legacy property + * "ipv4.routes" became the new property "ipv4.route-data"). In that case, the to/from D-Bus + * methods behave differently on the client and the daemon. + * + * The daemon will serialize both the legacy property and the new property to D-Bus. + * The client, will prefer the newer property (if it exists) when deserializing from D-Bus. + * + * Usually that scheme would fully suffice to support forward and backward compatibility. + * However, there is a problem. An old client (unaware of the new property) might get + * the profile, modify the old property, and send the entire profile back to the daemon. + * In this case, the old client does not know that the new property conflicts with the + * old property. The client also might try to preserve any unknown properties and send + * them back to the daemon. If the daemon now would prefer the new property, it would be wrong. + * + * The solution to this is that the daemon -- when both old and new property is set -- + * will prefer the old property. This is what _nm_setting_use_legacy_property() checks + * for. Consequently, a new client will not serialize both the old and the new property. + * This is done via "to_dbus_only_in_manager_process" flag. + * + * The downside of this scheme is that: + * + * - to/from D-Bus just got more complicated and behaves differently on the client + * and the daemon. + * - backward compatibility does not work with a newer client vs. and older daemon. + * This is the major downside. It's only not that severe, because we only deprecate + * properties seldom and only on major versions. Major version updates happen not + * often and they user might reboot (restart the daemon). + * + * The benefit is that the case with an older client and a newer daemon works, even + * if the client fetches a (new) profile, modifies only parts that it understands, + * and sends back the complete profile (including the new, unmodified properties). + */ if (!connection_dict) { /* we also allow the caller to provide no connection_dict. @@ -626,19 +667,24 @@ _nm_setting_use_legacy_property(NMSetting *setting, g_return_val_if_fail(setting_dict != NULL, FALSE); - /* If the new property isn't set, we have to use the legacy property. */ - value = g_variant_lookup_value(setting_dict, new_property, NULL); - if (!value) - return TRUE; - nm_clear_pointer(&value, g_variant_unref); + if (!_nm_utils_is_manager_process) { + /* The client will prefer the new property, unless it does not exist and + * the legacy property exists. */ + val_new = g_variant_lookup_value(setting_dict, new_property, NULL); + if (!val_new) { + val_leg = g_variant_lookup_value(setting_dict, legacy_property, NULL); + if (val_leg) + return TRUE; + } - /* Otherwise, clients always prefer new properties sent from the daemon. */ - if (!_nm_utils_is_manager_process) return FALSE; + } - /* The daemon prefers the legacy property if it exists. */ - value = g_variant_lookup_value(setting_dict, legacy_property, NULL); - return !!value; + /* The daemon prefers the old property (if it exists). */ + val_leg = g_variant_lookup_value(setting_dict, legacy_property, NULL); + if (val_leg) + return TRUE; + return FALSE; } /*****************************************************************************/ @@ -1256,7 +1302,7 @@ _nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_ (const NMValueStrv *) _nm_setting_get_private_field(setting, sett_info, property_info); if (!val->arr) return NULL; - return g_variant_new_strv((const char *const *) val->arr->data, val->arr->len); + return g_variant_new_strv(nm_g_array_data(val->arr), val->arr->len); } default: return nm_assert_unreachable_val(NULL); @@ -1310,7 +1356,7 @@ _nm_setting_property_to_dbus_fcn_gprop(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_n nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_ARRAY)); tmp_array = g_value_get_boxed(&prop_value); nm_assert(tmp_array); - return nm_g_variant_new_au((const guint32 *) tmp_array->data, tmp_array->len); + return nm_g_variant_new_au(nm_g_array_data(tmp_array), tmp_array->len); case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT: nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_HASH_TABLE)); return nm_strdict_to_variant_ass(g_value_get_boxed(&prop_value)); @@ -1752,31 +1798,36 @@ property_to_dbus(const NMSettInfoSetting *sett_info, || NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE) || property_info->property_type == &nm_sett_info_propert_type_setting_name); - if (property_info->param_spec && !ignore_flags - && !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS)) { - if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_LEGACY) - && !_nm_utils_is_manager_process) + if (ignore_flags) { + /* We are called from _nm_setting_property_compare_fcn_default(). We want + * to serialize the property, and ignore the flags. */ + } else { + if (property_info->to_dbus_only_in_manager_process && !_nm_utils_is_manager_process) return NULL; - if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) { - NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE; + if (property_info->param_spec + && !NM_FLAGS_HAS(property_info->param_spec->flags, + NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS)) { + if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) { + NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE; + + if (NM_FLAGS_ANY(flags, + NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED + | NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED + | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)) { + if (!nm_setting_get_secret_flags(setting, + property_info->param_spec->name, + &f, + NULL)) + return NULL; + } - if (NM_FLAGS_ANY(flags, - NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED - | NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED - | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)) { - if (!nm_setting_get_secret_flags(setting, - property_info->param_spec->name, - &f, - NULL)) + if (!_nm_connection_serialize_secrets(flags, f)) + return NULL; + } else { + if (!_nm_connection_serialize_non_secret(flags)) return NULL; } - - if (!_nm_connection_serialize_secrets(flags, f)) - return NULL; - } else { - if (!_nm_connection_serialize_non_secret(flags)) - return NULL; } } @@ -3196,6 +3247,12 @@ _nm_setting_clear_secrets(NMSetting *setting, /** * _nm_setting_need_secrets: * @setting: the #NMSetting + * @check_rerequest: If %TRUE: the stored secrets might be wrong and the agent + * should query the user for the correct credentials. If an #NMSetting knows + * that this cannot be the case it should *not* return the corresponding + * setting object. Otherwise it should always return it, even if it is not + * missing. + * If %FALSE: only return it when it is missing. * * Returns an array of property names for each secret which may be required * to make a successful connection. The returned hints are only intended as a @@ -3208,14 +3265,14 @@ _nm_setting_clear_secrets(NMSetting *setting, * free the elements. **/ GPtrArray * -_nm_setting_need_secrets(NMSetting *setting) +_nm_setting_need_secrets(NMSetting *setting, gboolean check_rerequest) { GPtrArray *secrets = NULL; g_return_val_if_fail(NM_IS_SETTING(setting), NULL); if (NM_SETTING_GET_CLASS(setting)->need_secrets) - secrets = NM_SETTING_GET_CLASS(setting)->need_secrets(setting); + secrets = NM_SETTING_GET_CLASS(setting)->need_secrets(setting, check_rerequest); return secrets; } @@ -3513,7 +3570,7 @@ nm_setting_to_string(NMSetting *setting) } static GVariant * -_nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +depreated_interface_name_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { NMSettingConnection *s_con; @@ -3533,8 +3590,9 @@ _nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .to_dbus_fcn = - _nm_setting_get_deprecated_virtual_interface_name, ); + .to_dbus_fcn = depreated_interface_name_to_dbus, + /* from_dbus_fcn() is handled by the connection.interface-name setter. + * See nm_setting_connection_no_interface_name(). */ ); const NMSettInfoPropertType nm_sett_info_propert_type_setting_name = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING, @@ -4066,6 +4124,233 @@ nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 v /*****************************************************************************/ +G_DEFINE_BOXED_TYPE(NMRange, nm_range, nm_range_ref, (GBoxedFreeFunc) nm_range_unref) + +static gboolean +NM_IS_RANGE(const NMRange *self) +{ + return self && self->refcount > 0; +} + +/** + * nm_range_new: + * @start: the first element of the range + * @end: the last element of the range, must be greater than or equal + * to @start. + * + * Creates a new #NMRange object for the given range. Setting @end + * equal to @start creates a single-element range. + * + * Returns: (transfer full): the new #NMRange object. + * + * Since: 1.42 + **/ +NMRange * +nm_range_new(guint64 start, guint64 end) +{ + NMRange *range; + + g_return_val_if_fail(start <= end, NULL); + + range = g_slice_new(NMRange); + *range = (NMRange){ + .refcount = 1, + .start = start, + .end = end, + }; + + return range; +} + +/** + * nm_range_ref: + * @range: the #NMRange + * + * Increases the reference count of the object. + * This is thread-safe. + * + * Returns: the input argument @range object. + * + * Since: 1.42 + **/ +NMRange * +nm_range_ref(const NMRange *range) +{ + g_return_val_if_fail(NM_IS_RANGE(range), NULL); + + nm_assert(range->refcount < G_MAXINT); + + g_atomic_int_inc(&((NMRange *) range)->refcount); + return (NMRange *) range; +} + +/** + * nm_range_unref: + * @range: the #NMRange + * + * Decreases the reference count of the object. If the reference count + * reaches zero the object will be destroyed. + * This is thread-safe. + * + * Since: 1.42 + **/ +void +nm_range_unref(const NMRange *range) +{ + g_return_if_fail(NM_IS_RANGE(range)); + + if (g_atomic_int_dec_and_test(&((NMRange *) range)->refcount)) + nm_g_slice_free((NMRange *) range); +} + +/** + * nm_range_cmp: + * @a: a #NMRange + * @b: another #NMRange + * + * Compare two ranges. + * + * Returns: zero if the two instances are equivalent or + * a non-zero integer otherwise. This defines a total ordering + * over the ranges. + * + * Since: 1.42 + **/ +int +nm_range_cmp(const NMRange *a, const NMRange *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, start); + NM_CMP_FIELD(a, b, end); + + return 0; +} + +/** + * nm_range_get_range: + * @range: the #NMRange + * @start: (out): location to store the start value + * @end: (out): location to store the end value + * + * Gets the start and end values for the range. + * + * Returns: %TRUE if the range contains more than one + * element, %FALSE otherwise. + * + * Since: 1.42 + **/ +gboolean +nm_range_get_range(const NMRange *range, guint64 *start, guint64 *end) +{ + /* with LTO and optimization, the compiler complains that the + * output variables are not initialized. In practice, the function + * only sets the output on success. But make the compiler happy. + */ + NM_SET_OUT(start, 0); + NM_SET_OUT(end, 0); + + g_return_val_if_fail(NM_IS_RANGE(range), 0); + + NM_SET_OUT(start, range->start); + NM_SET_OUT(end, range->end); + + return range->start != range->end; +} + +/** + * nm_range_to_str: + * @range: the %NMRange + * + * Convert a %NMRange to a string. + * + * Returns: (transfer full): a string representing the range. + * + * Since: 1.42 + */ +char * +nm_range_to_str(const NMRange *range) +{ + char buf[200]; + char *b = buf; + gsize l = sizeof(buf); + + g_return_val_if_fail(NM_IS_RANGE(range), NULL); + + nm_strbuf_append(&b, &l, "%" G_GUINT64_FORMAT, range->start); + if (range->start != range->end) + nm_strbuf_append(&b, &l, "-%" G_GUINT64_FORMAT, range->end); + + nm_assert(l > 0); + return nm_memdup_nul(buf, sizeof(buf) - l); +} + +/** + * nm_range_from_str: + * @str: the string representation of a range + * @error: (out) (allow-none): location to store the error on failure + * + * Parses the string representation of the range to create a %NMRange + * instance. + * + * Returns: (transfer full): the %NMRange or %NULL + * + * Since: 1.42 + */ +NMRange * +nm_range_from_str(const char *str, GError **error) +{ + gs_free char *str_free = NULL; + guint64 start; + guint64 end = 0; + char *c; + + g_return_val_if_fail(str, NULL); + g_return_val_if_fail(!error || !*error, NULL); + + c = strchr(str, '-'); + if (c) { + str = nm_strndup_a(300, str, c - str, &str_free); + c++; + } + + start = _nm_utils_ascii_str_to_uint64(str, 10, 0, G_MAXUINT64, 0); + if (errno != 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range start '%s'", + str); + return NULL; + } + + if (c) { + end = _nm_utils_ascii_str_to_uint64(c, 10, 0, G_MAXUINT64, 0); + if (errno != 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range end '%s'", + c); + return NULL; + } + if (end < start) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT + ", start must be less than or equal to end", + start, + end); + return NULL; + } + } else + end = start; + + return nm_range_new(start, end); +} + +/*****************************************************************************/ + static void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { |