diff options
Diffstat (limited to 'src/libnm-core-impl/nm-setting-ovs-other-config.c')
-rw-r--r-- | src/libnm-core-impl/nm-setting-ovs-other-config.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/src/libnm-core-impl/nm-setting-ovs-other-config.c b/src/libnm-core-impl/nm-setting-ovs-other-config.c new file mode 100644 index 0000000000..ca46ae9576 --- /dev/null +++ b/src/libnm-core-impl/nm-setting-ovs-other-config.c @@ -0,0 +1,403 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2020, 2022 Red Hat, Inc. + */ + +#include "libnm-core-impl/nm-default-libnm-core.h" + +#include "nm-setting-ovs-other-config.h" + +#include "nm-setting-private.h" +#include "nm-utils-private.h" +#include "nm-connection-private.h" + +#define MAX_NUM_KEYS 256 + +/*****************************************************************************/ + +/** + * SECTION:nm-setting-ovs-other-config + * @short_description: Other-config settings for OVS + * + * The #NMSettingOvsOtherConfig object is a #NMSetting subclass that allows to + * configure other_config settings for OVS. See also "other_config" in the + * "ovs-vswitchd.conf.db" manual for the keys that OVS supports. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsOtherConfig, PROP_DATA, ); + +typedef struct { + GHashTable *data; + const char **data_keys; +} NMSettingOvsOtherConfigPrivate; + +/** + * NMSettingOvsOtherConfig: + * + * OVS Other Config Settings + * + * Since: 1.42 + */ +struct _NMSettingOvsOtherConfig { + NMSetting parent; + NMSettingOvsOtherConfigPrivate _priv; +}; + +struct _NMSettingOvsOtherConfigClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE(NMSettingOvsOtherConfig, nm_setting_ovs_other_config, NM_TYPE_SETTING) + +#define NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMSettingOvsOtherConfig, NM_IS_SETTING_OVS_OTHER_CONFIG) + +/*****************************************************************************/ + +static GHashTable * +_create_data_hash(void) +{ + return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); +} + +GHashTable * +_nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *self) +{ + return NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self)->data; +} + +/** + * nm_setting_ovs_other_config_get_data_keys: + * @setting: the #NMSettingOvsOtherConfig + * @out_len: (out): the length of the returned array + * + * Returns: (array length=out_len) (transfer none): a + * %NULL-terminated array containing each key from the table. + * + * Since: 1.42 + **/ +const char *const * +nm_setting_ovs_other_config_get_data_keys(NMSettingOvsOtherConfig *setting, guint *out_len) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + NM_SET_OUT(out_len, 0); + + g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (priv->data_keys) { + NM_SET_OUT(out_len, g_hash_table_size(priv->data)); + return priv->data_keys; + } + + priv->data_keys = nm_strdict_get_keys(priv->data, TRUE, out_len); + + /* don't return %NULL, but hijack the @data_keys fields as a pseudo + * empty strv array. */ + return priv->data_keys ?: ((const char **) &priv->data_keys); +} + +/*****************************************************************************/ + +/** + * nm_setting_ovs_other_config_get_data: + * @setting: the #NMSettingOvsOtherConfig instance + * @key: the other-config to lookup + * + * Since: 1.42 + * + * Returns: (transfer none): the value associated with @key or %NULL if no such + * value exists. + */ +const char * +nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *setting, const char *key) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL); + g_return_val_if_fail(key, NULL); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (!priv->data) + return NULL; + + return g_hash_table_lookup(priv->data, key); +} + +/** + * nm_setting_ovs_other_config_set_data: + * @setting: the #NMSettingOvsOtherConfig instance + * @key: the key to set + * @val: (allow-none): the value to set or %NULL to clear a key. + * + * Since: 1.42 + */ +void +nm_setting_ovs_other_config_set_data(NMSettingOvsOtherConfig *setting, + const char *key, + const char *val) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + g_return_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self)); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (!val) { + if (priv->data && g_hash_table_remove(priv->data, key)) + goto out_changed; + return; + } + + if (priv->data) { + const char *val2; + + if (g_hash_table_lookup_extended(priv->data, key, NULL, (gpointer *) &val2)) { + if (nm_streq(val, val2)) + return; + } + } else + priv->data = _create_data_hash(); + + g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val)); + +out_changed: + nm_clear_g_free(&priv->data_keys); + _notify(self, PROP_DATA); +} + +/*****************************************************************************/ + +static gboolean +verify(NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(setting); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (priv->data) { + gs_free_error GError *local = NULL; + const char *const *keys; + guint len; + guint i; + + keys = nm_setting_ovs_other_config_get_data_keys(self, &len); + + for (i = 0; i < len; i++) { + const char *key = keys[i]; + const char *val = g_hash_table_lookup(priv->data, key); + + if (!nm_setting_ovs_other_config_check_key(key, &local)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid key \"%s\": %s"), + key, + local->message); + } else if (!nm_setting_ovs_other_config_check_val(val, &local)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid value for \"%s\": %s"), + key, + local->message); + } else + continue; + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_DATA); + return FALSE; + } + } + + if (priv->data && g_hash_table_size(priv->data) > MAX_NUM_KEYS) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("maximum number of entries reached (%u instead of %u)"), + g_hash_table_size(priv->data), + (unsigned) MAX_NUM_KEYS); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_DATA); + return FALSE; + } + + if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_OTHER_CONFIG, + connection, + error)) + return FALSE; + + return TRUE; +} + +static NMTernary +compare_fcn_data(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) +{ + NMSettingOvsOtherConfigPrivate *priv; + NMSettingOvsOtherConfigPrivate *pri2; + + if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) + return NM_TERNARY_DEFAULT; + + if (!set_b) + return TRUE; + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_a)); + pri2 = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_b)); + return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + GHashTableIter iter; + GHashTable *data; + const char *key; + const char *val; + + switch (prop_id) { + case PROP_DATA: + data = _create_data_hash(); + if (priv->data) { + g_hash_table_iter_init(&iter, priv->data); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(data, g_strdup(key), g_strdup(val)); + } + g_value_take_boxed(value, data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_DATA: + { + gs_unref_hashtable GHashTable *old = NULL; + GHashTableIter iter; + GHashTable *data; + const char *key; + const char *val; + + nm_clear_g_free(&priv->data_keys); + + old = g_steal_pointer(&priv->data); + + data = g_value_get_boxed(value); + if (nm_g_hash_table_size(data) <= 0) + return; + + priv->data = _create_data_hash(); + g_hash_table_iter_init(&iter, data); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val)); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_other_config_init(NMSettingOvsOtherConfig *self) +{} + +/** + * nm_setting_ovs_other_config_new: + * + * Creates a new #NMSettingOvsOtherConfig object with default values. + * + * Returns: (transfer full) (type NMSettingOvsOtherConfig): the new empty + * #NMSettingOvsOtherConfig object + * + * Since: 1.42 + */ +NMSetting * +nm_setting_ovs_other_config_new(void) +{ + return g_object_new(NM_TYPE_SETTING_OVS_OTHER_CONFIG, NULL); +} + +static void +finalize(GObject *object) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + g_free(priv->data_keys); + if (priv->data) + g_hash_table_unref(priv->data); + + G_OBJECT_CLASS(nm_setting_ovs_other_config_parent_class)->finalize(object); +} + +static void +nm_setting_ovs_other_config_class_init(NMSettingOvsOtherConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + setting_class->verify = verify; + + /** + * NMSettingOvsOtherConfig:data: (type GHashTable(utf8,utf8)) + * + * A dictionary of key/value pairs with other_config settings for OVS. + * See also "other_config" in the "ovs-vswitchd.conf.db" manual for the keys + * that OVS supports. + * + * Since: 1.42 + **/ + obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_OVS_OTHER_CONFIG_DATA, + "", + "", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_properties_override_gobj( + properties_override, + obj_properties[PROP_DATA], + NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("a{ss}"), + .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus, + .typdata_to_dbus.gprop_type = + NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT, + .compare_fcn = compare_fcn_data, + .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, + .from_dbus_is_full = TRUE)); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + NULL, + properties_override, + 0); +} |