diff options
Diffstat (limited to 'src/libnm-core-impl/nm-setting.c')
| -rw-r--r-- | src/libnm-core-impl/nm-setting.c | 360 |
1 files changed, 228 insertions, 132 deletions
diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index 276dfd34..758338e5 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -11,7 +11,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-ref-string.h" #include "libnm-glib-aux/nm-secret-utils.h" -#include "nm-property-compare.h" #include "nm-setting-private.h" #include "nm-utils-private.h" #include "nm-utils.h" @@ -58,14 +57,13 @@ typedef struct { NM_GOBJECT_PROPERTIES_DEFINE(NMSetting, PROP_NAME, ); -typedef struct { +typedef struct _NMSettingPrivate { GenData *gendata; } NMSettingPrivate; G_DEFINE_ABSTRACT_TYPE(NMSetting, nm_setting, G_TYPE_OBJECT) -#define NM_SETTING_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_SETTING, NMSettingPrivate)) +#define NM_SETTING_GET_PRIVATE(o) _NM_GET_PRIVATE_PTR(o, NMSetting, NM_IS_SETTING) /*****************************************************************************/ @@ -165,7 +163,7 @@ _nm_setting_slave_type_is_valid(const char *slave_type, const char **out_port_ty /*****************************************************************************/ -static const NMSettInfoProperty * +_nm_unused static const NMSettInfoProperty * _nm_sett_info_property_find_in_array(const NMSettInfoProperty *properties, guint len, const char *name) @@ -184,9 +182,8 @@ _nm_properties_override_assert(const NMSettInfoProperty *prop_info) { #if NM_MORE_ASSERTS nm_assert(prop_info); - nm_assert((!!prop_info->name) != (!!prop_info->param_spec)); - nm_assert(!prop_info->param_spec || !prop_info->name - || nm_streq0(prop_info->name, prop_info->param_spec->name)); + nm_assert(prop_info->name); + nm_assert(!prop_info->param_spec || prop_info->name == prop_info->param_spec->name); if (prop_info->property_type) { const NMSettInfoPropertType *property_type = prop_info->property_type; @@ -209,6 +206,34 @@ _nm_properties_override_assert(const NMSettInfoProperty *prop_info) return TRUE; } +GArray * +_nm_sett_info_property_override_create_array_sized(guint reserved_size) +{ + GArray *properties_override; + GParamSpec *param_spec; + + /* pre-allocate a relatively large buffer to avoid frequent re-allocations. + * Note that the buffer is only short-lived and will be destroyed by + * _nm_setting_class_commit(). */ + properties_override = + g_array_sized_new(FALSE, FALSE, sizeof(NMSettInfoProperty), reserved_size); + + /* The "name" property is special because it's defined in the + * parent class NMSetting. We add the property info right here. + */ + param_spec = obj_properties[PROP_NAME]; + nm_assert(param_spec); + nm_assert(nm_streq0(param_spec->name, NM_SETTING_NAME)); + nm_assert(param_spec + == g_object_class_find_property(g_type_class_peek(NM_TYPE_SETTING), NM_SETTING_NAME)); + nm_assert(!NM_FLAGS_HAS(param_spec->flags, G_PARAM_WRITABLE)); + _nm_properties_override_gobj(properties_override, + param_spec, + &nm_sett_info_propert_type_setting_name); + + return properties_override; +} + static NMSettInfoSetting _sett_info_settings[_NM_META_SETTING_TYPE_NUM]; const NMSettInfoSetting * @@ -224,10 +249,8 @@ _property_infos_sort_cmp_setting_connection(gconstpointer p_a, { 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); + nm_assert(a->name && b->name && !nm_streq(a->name, b->name)); #define CMP_AND_RETURN(n_a, n_b, name) \ G_STMT_START \ @@ -240,15 +263,14 @@ _property_infos_sort_cmp_setting_connection(gconstpointer p_a, 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); - } + 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; + NM_CMP_FIELD_STR(a, b, name); + return nm_assert_unreachable_val(0); } static const NMSettInfoProperty *const * @@ -306,12 +328,14 @@ _nm_setting_class_commit(NMSettingClass *setting_class, GArray *properties_override, gint16 private_offset) { - NMSettInfoSetting *sett_info; - gs_free GParamSpec **property_specs = NULL; - guint n_property_specs; + NMSettInfoSetting *sett_info; +#if NM_MORE_ASSERTS > 10 + gs_free GParamSpec **property_specs = NULL; + guint n_property_specs; + guint i; +#endif NMSettInfoPropertLookupByParamSpec *lookup_by_iter; guint override_len; - guint i; guint16 j; nm_assert(NM_IS_SETTING_CLASS(setting_class)); @@ -325,37 +349,28 @@ _nm_setting_class_commit(NMSettingClass *setting_class, nm_assert(!sett_info->property_infos_len); nm_assert(!sett_info->property_infos); - property_specs = - g_object_class_list_properties(G_OBJECT_CLASS(setting_class), &n_property_specs); - - if (!properties_override) { - override_len = 0; - properties_override = _nm_sett_info_property_override_create_array_sized(n_property_specs); - } else { - override_len = properties_override->len; + if (!properties_override) + properties_override = _nm_sett_info_property_override_create_array_sized(1); - for (i = 0; i < override_len; i++) { - NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); + override_len = properties_override->len; - nm_assert((!!p->name) != (!!p->param_spec)); - - if (!p->name) { - nm_assert(p->param_spec); - p->name = p->param_spec->name; - } else - nm_assert(!p->param_spec); - } - } + nm_assert(override_len > 0); #if NM_MORE_ASSERTS > 10 + property_specs = + g_object_class_list_properties(G_OBJECT_CLASS(setting_class), &n_property_specs); + /* assert that properties_override is constructed consistently. */ for (i = 0; i < override_len; i++) { const NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); gboolean found = FALSE; guint k; + nm_assert(p->name); + nm_assert(!p->param_spec || p->name == p->param_spec->name); + nm_assert(!_nm_sett_info_property_find_in_array( - nm_g_array_index_p(properties_override, NMSettInfoProperty, 0), + nm_g_array_first_p(properties_override, NMSettInfoProperty), i, p->name)); for (k = 0; k < n_property_specs; k++) { @@ -367,79 +382,38 @@ _nm_setting_class_commit(NMSettingClass *setting_class, } nm_assert(found == (p->param_spec != NULL)); } -#endif for (i = 0; i < n_property_specs; i++) { - const char *name = property_specs[i]->name; - NMSettInfoProperty *p; - - if (_nm_sett_info_property_find_in_array( - nm_g_array_index_p(properties_override, NMSettInfoProperty, 0), - override_len, - name)) - continue; - - p = nm_g_array_append_new(properties_override, NMSettInfoProperty); - memset(p, 0, sizeof(*p)); - p->name = name; - p->param_spec = property_specs[i]; + const GParamSpec *param_spec = property_specs[i]; + const char *name = param_spec->name; + const NMSettInfoProperty *p; + + p = _nm_sett_info_property_find_in_array( + nm_g_array_first_p(properties_override, NMSettInfoProperty), + override_len, + name); + nm_assert(p); + nm_assert(p->param_spec); + nm_assert(p->param_spec == param_spec); } for (i = 0; i < properties_override->len; i++) { NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); - GType vtype; - - if (p->property_type) - goto has_property_type; - - nm_assert(p->param_spec); - - vtype = p->param_spec->value_type; - if (vtype == G_TYPE_STRING) { - /* The "name" property is a bit special because it's defined in the - * parent class NMSetting. We set the property_type here, because - * it's more convenient (albeit a bit ugly). - * - * FIXME: let _nm_sett_info_property_override_create_array() always add - * the handling of the name property.*/ - nm_assert(nm_streq(p->name, NM_SETTING_NAME)); - nm_assert(!NM_FLAGS_HAS(p->param_spec->flags, G_PARAM_WRITABLE)); - p->property_type = &nm_sett_info_propert_type_setting_name; - goto has_property_type; - } - - if (vtype == G_TYPE_STRV) - p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP( - G_VARIANT_TYPE_STRING_ARRAY, - .compare_fcn = _nm_setting_property_compare_fcn_default, - .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, - .from_dbus_is_full = TRUE); - else - nm_assert_not_reached(); - -has_property_type: nm_assert(p->property_type); nm_assert(p->property_type->dbus_type); nm_assert(g_variant_type_string_is_valid((const char *) p->property_type->dbus_type)); + nm_assert(p->name); + nm_assert(!p->param_spec || nm_streq0(p->name, p->param_spec->name)); } +#endif G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMSettInfoProperty, name) == 0); g_array_sort(properties_override, nm_strcmp_p); setting_class->setting_info = &nm_meta_setting_infos[meta_type]; sett_info->setting_class = setting_class; - - if (private_offset == NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS) { - int o; - - o = g_type_class_get_instance_private_offset(setting_class); - nm_assert(o != NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS); - nm_assert(o > G_MININT16); - nm_assert(o < 0); - private_offset = o; - } - sett_info->private_offset = private_offset; + sett_info->private_offset = private_offset; if (detail) sett_info->detail = *detail; @@ -708,10 +682,10 @@ _property_direct_set_string(const NMSettInfoSetting *sett_info, + (!!property_info->direct_string_is_refstr) + (property_info->direct_set_string_mac_address_len > 0) + (property_info->direct_set_string_ip_address_addr_family != 0)) - <= (property_info->direct_hook.set_string_fcn ? 0 : 1)); + <= (property_info->direct_set_fcn.set_string ? 0 : 1)); - if (property_info->direct_hook.set_string_fcn) { - return property_info->direct_hook.set_string_fcn(sett_info, property_info, setting, src); + if (property_info->direct_set_fcn.set_string) { + return property_info->direct_set_fcn.set_string(sett_info, property_info, setting, src); } dst = _nm_setting_get_private_field(setting, sett_info, property_info); @@ -754,6 +728,24 @@ out_take: return nm_strdup_reset_take(dst, s); } +static gboolean +_property_direct_set_strv(const NMSettInfoSetting *sett_info, + const NMSettInfoProperty *property_info, + NMSetting *setting, + const char *const *strv) +{ + NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + if (!property_info->direct_strv_preserve_empty && strv && !strv[0]) + strv = NULL; + + if (nm_strvarray_equal_strv(p_val->arr, strv, -1)) + return FALSE; + + nm_strvarray_set_strv_full(&p_val->arr, strv, property_info->direct_strv_preserve_empty); + return TRUE; +} + void _nm_setting_property_get_property_direct(GObject *object, guint prop_id, @@ -842,14 +834,19 @@ _nm_setting_property_get_property_direct(GObject *object, { const NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(p_val->arr, NULL)); + g_value_take_boxed( + value, + nm_strvarray_get_strv_full_dup(p_val->arr, + NULL, + FALSE, + property_info->direct_strv_preserve_empty)); return; } default: goto out_fail; } - return; + nm_assert_not_reached(); out_fail: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -981,28 +978,22 @@ _nm_setting_property_set_property_direct(GObject *object, goto out_notify; } case NM_VALUE_TYPE_STRV: - { - NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); - const char *const *v; - - v = g_value_get_boxed(value); - if (nm_strvarray_equal_strv(p_val->arr, v, -1)) + if (!_property_direct_set_strv(sett_info, property_info, setting, g_value_get_boxed(value))) return; - - nm_strvarray_set_strv(&p_val->arr, v); goto out_notify; - } default: goto out_fail; } - return; + nm_assert_not_reached(); out_notify: - /* If explicit-notify would be set, we would need to emit g_object_notify_by_pspec(). - * - * Currently we never set that, also because we still support glib 2.40. */ - nm_assert(!NM_FLAGS_HAS(pspec->flags, 1 << 30 /* G_PARAM_EXPLICIT_NOTIFY */)); + nm_assert(NM_FLAGS_HAS(pspec->flags, G_PARAM_EXPLICIT_NOTIFY)); + + nm_gobject_notify_together_by_pspec(object, + property_info->param_spec, + property_info->direct_also_notify); + return; out_fail: @@ -1306,6 +1297,11 @@ _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; + if (!property_info->direct_strv_preserve_empty && val->arr->len == 0) { + /* This property does not treat empty strv arrays special. No need + * to export the value on D-Bus. */ + return NULL; + } return g_variant_new_strv(nm_g_array_data(val->arr), val->arr->len); } default: @@ -1393,7 +1389,9 @@ _nm_setting_property_from_dbus_fcn_direct_mac_address(_NM_SETT_INFO_PROP_FROM_DB if (nm_strdup_reset_take(_nm_setting_get_private_field(setting, sett_info, property_info), length > 0 ? nm_utils_hwaddr_ntoa(array, length) : NULL)) { - g_object_notify_by_pspec(G_OBJECT(setting), property_info->param_spec); + nm_gobject_notify_together_by_pspec(setting, + property_info->param_spec, + property_info->direct_also_notify); } else *out_is_modified = FALSE; @@ -1670,7 +1668,6 @@ _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS } case NM_VALUE_TYPE_STRV: { - NMValueStrv *p_val; gs_free const char **ss = NULL; gsize ss_len; @@ -1681,13 +1678,10 @@ _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS ss = g_variant_get_strv(value, &ss_len); nm_assert(ss_len <= G_MAXUINT); + nm_assert(NM_PTRARRAY_LEN(ss) == ss_len); - p_val = _nm_setting_get_private_field(setting, sett_info, property_info); - - if (nm_strvarray_equal_strv(p_val->arr, ss, ss_len)) + if (!_property_direct_set_strv(sett_info, property_info, setting, ss)) goto out_unchanged; - - nm_strvarray_set_strv(&p_val->arr, ss); goto out_notify; } default: @@ -1701,7 +1695,9 @@ out_unchanged: out_notify: *out_is_modified = TRUE; - g_object_notify_by_pspec(G_OBJECT(setting), property_info->param_spec); + nm_gobject_notify_together_by_pspec(setting, + property_info->param_spec, + property_info->direct_also_notify); return TRUE; out_error_wrong_dbus_type: @@ -2409,17 +2405,87 @@ nm_setting_verify(NMSetting *setting, NMConnection *connection, GError **error) return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE; } +static gboolean +_verify_properties(NMSetting *setting, GError **error) +{ + NMSettingClass *klass = NM_SETTING_GET_CLASS(setting); + const NMSettInfoSetting *sett_info = _nm_setting_class_get_sett_info(klass); + guint16 i; + + if (!sett_info) + return TRUE; + + for (i = 0; i < sett_info->property_infos_len; i++) { + const NMSettInfoProperty *property_info = &sett_info->property_infos[i]; + + switch (property_info->property_type->direct_type) { + case NM_VALUE_TYPE_BOOL: + case NM_VALUE_TYPE_BYTES: + case NM_VALUE_TYPE_STRV: + case NM_VALUE_TYPE_ENUM: + case NM_VALUE_TYPE_FLAGS: + case NM_VALUE_TYPE_INT32: + case NM_VALUE_TYPE_INT64: + case NM_VALUE_TYPE_NONE: + case NM_VALUE_TYPE_UINT32: + case NM_VALUE_TYPE_UINT64: + break; + case NM_VALUE_TYPE_STRING: + { + const char *val; + + if (!property_info->direct_string_allow_empty + && (val = *((const char *const *) + _nm_setting_get_private_field(setting, sett_info, property_info))) + && val[0] == '\0') { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("cannot be empty")); + g_prefix_error(error, + "%s.%s: ", + klass->setting_info->setting_name, + property_info->name); + return FALSE; + } + break; + } + default: + nm_assert_not_reached(); + } + } + + return TRUE; +} + NMSettingVerifyResult _nm_setting_verify(NMSetting *setting, NMConnection *connection, GError **error) { + NMSettingClass *klass; + NMSettingVerifyResult r; + g_return_val_if_fail(NM_IS_SETTING(setting), NM_SETTING_VERIFY_ERROR); g_return_val_if_fail(!connection || NM_IS_CONNECTION(connection), NM_SETTING_VERIFY_ERROR); g_return_val_if_fail(!error || *error == NULL, NM_SETTING_VERIFY_ERROR); - if (NM_SETTING_GET_CLASS(setting)->verify) - return NM_SETTING_GET_CLASS(setting)->verify(setting, connection, error); + klass = NM_SETTING_GET_CLASS(setting); - return NM_SETTING_VERIFY_SUCCESS; + if (!klass->verify) + return NM_SETTING_VERIFY_SUCCESS; + + r = klass->verify(setting, connection, error); + + if (r != NM_SETTING_VERIFY_ERROR) { + gs_free_error GError *local = NULL; + + if (!_verify_properties(setting, &local)) { + g_clear_error(error); + g_propagate_error(error, g_steal_pointer(&local)); + r = NM_SETTING_VERIFY_ERROR; + } + } + + return r; } /** @@ -2570,7 +2636,9 @@ _nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_ nm_assert(NM_IN_SET(property_info->property_type->to_dbus_fcn, _nm_setting_property_to_dbus_fcn_direct, - _nm_setting_property_to_dbus_fcn_direct_mac_address)); + _nm_setting_property_to_dbus_fcn_direct_mac_address, + _nm_setting_connection_controller_to_dbus, + _nm_setting_connection_port_type_to_dbus)); if (!property_info->param_spec) return nm_assert_unreachable_val(NM_TERNARY_DEFAULT); @@ -2604,8 +2672,21 @@ _nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_ case NM_VALUE_TYPE_BYTES: return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b)); case NM_VALUE_TYPE_STRV: - return nm_strvarray_equal(((const NMValueStrv *) p_a)->arr, - ((const NMValueStrv *) p_b)->arr); + { + const NMValueStrv *v_a = p_a; + const NMValueStrv *v_b = p_b; + const GArray *a = v_a->arr; + const GArray *b = v_b->arr; + + if (!property_info->direct_strv_preserve_empty) { + /* NULL and empty are treated identical. Coerce to NULL. */ + if (a && a->len == 0) + a = NULL; + if (b && b->len == 0) + b = NULL; + } + return nm_strvarray_equal(a, b); + } default: return nm_assert_unreachable_val(TRUE); } @@ -2643,7 +2724,7 @@ _nm_setting_property_compare_fcn_default(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm NM_CONNECTION_SERIALIZE_ALL, NULL, TRUE); - return nm_property_compare(value1, value2) == 0; + return nm_g_variant_equal(value1, value2); } } @@ -3617,6 +3698,15 @@ const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_u = /* No functions set. This property type is to silently ignore the value on D-Bus. */ .compare_fcn = _nm_setting_property_compare_fcn_ignore); +/* This should not be used for new strv properties. Use nm_sett_info_propert_type_direct_strv. + * + * FIXME: existing properties should migrate to nm_sett_info_propert_type_direct_strv. */ +const NMSettInfoPropertType nm_sett_info_propert_type_gprop_strv_oldstyle = + NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(G_VARIANT_TYPE_STRING_ARRAY, + .compare_fcn = _nm_setting_property_compare_fcn_default, + .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, + .from_dbus_is_full = TRUE); + const NMSettInfoPropertType nm_sett_info_propert_type_direct_boolean = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BOOLEAN, .direct_type = NM_VALUE_TYPE_BOOL, @@ -4374,7 +4464,13 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) static void nm_setting_init(NMSetting *setting) -{} +{ + NMSettingPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(setting, NM_TYPE_SETTING, NMSettingPrivate); + + setting->_priv = priv; +} static void constructed(GObject *object) |