Commit 002b19f4 authored by Dan Winship's avatar Dan Winship

libnm-core: add dbus-only properties to NMSettingClass

Add _nm_setting_class_add_dbus_only_property(), for declaring
properties that appear in the D-Bus serialization, but which don't
correspond to GObject properties.

Since some property overrides will require examining settings other
than the setting that they are on (eg, the value of
802-11-wireless.security depends on whether an
NMSettingWirelessSecurity setting is present, and
NMSettingConnection:interface-name might sometimes be set from, eg,
bond.interface-name), we also update _nm_setting_to_dbus() to take the
full NMConnection as an argument, and _nm_setting_new_from_dbus() to
take the full connection hash.

Additionally, with some deprecated properties, we'll want to validate
them on construction, but we don't need to keep the value around after
that. So allow _nm_setting_new_from_dbus() to return a verification
error directly, so we don't need to store the value until the verify()
call.
parent 504c292d
......@@ -1268,7 +1268,7 @@ nm_connection_to_dbus (NMConnection *connection, NMConnectionSerializationFlags
while (g_hash_table_iter_next (&iter, &key, &data)) {
NMSetting *setting = NM_SETTING (data);
setting_hash = _nm_setting_to_dbus (setting, flags);
setting_hash = _nm_setting_to_dbus (setting, connection, flags);
if (setting_hash)
g_hash_table_insert (ret, g_strdup (nm_setting_get_name (setting)), setting_hash);
}
......
......@@ -116,9 +116,28 @@ gboolean _nm_setting_slave_type_is_valid (const char *slave_type, const char **o
const char * _nm_setting_slave_type_detect_from_settings (GSList *all_settings, NMSetting **out_s_port);
GHashTable *_nm_setting_to_dbus (NMSetting *setting,
NMConnection *connection,
NMConnectionSerializationFlags flags);
NMSetting *_nm_setting_new_from_dbus (GType setting_type,
GHashTable *hash);
GHashTable *setting_hash,
GHashTable *connection_hash,
GError **error);
typedef gboolean (*NMSettingPropertyGetFunc) (NMSetting *setting,
NMConnection *connection,
const char *property,
GValue *value);
typedef gboolean (*NMSettingPropertySetFunc) (NMSetting *setting,
GHashTable *connection_hash,
const char *property,
const GValue *value,
GError **error);
void _nm_setting_class_add_dbus_only_property (NMSettingClass *setting_class,
const char *property_name,
GType dbus_type,
NMSettingPropertyGetFunc get_func,
NMSettingPropertySetFunc set_func);
#endif /* NM_SETTING_PRIVATE_H */
......@@ -372,18 +372,193 @@ _nm_setting_slave_type_detect_from_settings (GSList *all_settings, NMSetting **o
/*************************************************************/
typedef struct {
const char *name;
GParamSpec *param_spec;
GType dbus_type;
NMSettingPropertyGetFunc get_func;
NMSettingPropertySetFunc set_func;
} NMSettingProperty;
static GQuark setting_property_overrides_quark;
static GQuark setting_properties_quark;
static NMSettingProperty *
find_property (GArray *properties, const char *name)
{
NMSettingProperty *property;
int i;
if (!properties)
return NULL;
for (i = 0; i < properties->len; i++) {
property = &g_array_index (properties, NMSettingProperty, i);
if (strcmp (name, property->name) == 0)
return property;
}
return NULL;
}
static void
add_property_override (NMSettingClass *setting_class,
const char *property_name,
GType dbus_type,
NMSettingPropertyGetFunc get_func,
NMSettingPropertySetFunc set_func)
{
GType setting_type = G_TYPE_FROM_CLASS (setting_class);
GArray *overrides;
NMSettingProperty override;
g_return_if_fail (g_type_get_qdata (setting_type, setting_properties_quark) == NULL);
memset (&override, 0, sizeof (override));
override.name = property_name;
override.dbus_type = dbus_type;
override.get_func = get_func;
override.set_func = set_func;
overrides = g_type_get_qdata (setting_type, setting_property_overrides_quark);
if (!overrides) {
overrides = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
g_type_set_qdata (setting_type, setting_property_overrides_quark, overrides);
}
g_return_if_fail (find_property (overrides, property_name) == NULL);
g_array_append_val (overrides, override);
}
/**
* _nm_setting_class_add_dbus_only_property:
* @setting_class: the setting class
* @property_name: the name of the property to override
* @dbus_type: the type of the property
* @get_func: (allow-none): function to call to get the value of the property
* @set_func: (allow-none): function to call to set the value of the property
*
* Registers a property named @property_name, which will be used in the D-Bus
* serialization of objects of @setting_class, but which does not correspond to
* a #GObject property.
*
* When serializing a setting to D-Bus, @get_func will be called to get the
* property's value. If it returns %TRUE, the value will be added to the hash,
* and if %FALSE, it will not. (If @get_func is %NULL, the property will always
* be omitted in the serialization.)
*
* When deserializing a D-Bus representation into a setting, if @property_name
* is present, then @set_func will be called to set (and/or verify) it. If it
* returns %TRUE, the value is considered to have been successfully set; if it
* returns %FALSE then the deserializing operation as a whole will fail with the
* returned #GError. (If @set_func is %NULL then the property will be ignored
* when deserializing.)
*/
void
_nm_setting_class_add_dbus_only_property (NMSettingClass *setting_class,
const char *property_name,
GType dbus_type,
NMSettingPropertyGetFunc get_func,
NMSettingPropertySetFunc set_func)
{
g_return_if_fail (NM_IS_SETTING_CLASS (setting_class));
g_return_if_fail (property_name != NULL);
/* Must not match any GObject property. */
g_return_if_fail (!g_object_class_find_property (G_OBJECT_CLASS (setting_class), property_name));
add_property_override (setting_class,
property_name, dbus_type,
get_func, set_func);
}
static GArray *
nm_setting_class_ensure_properties (NMSettingClass *setting_class)
{
GType type = G_TYPE_FROM_CLASS (setting_class), otype;
NMSettingProperty property, *override;
GArray *overrides, *type_overrides, *properties;
GParamSpec **property_specs;
guint n_property_specs, i;
properties = g_type_get_qdata (type, setting_properties_quark);
if (properties)
return properties;
/* Build overrides array from @setting_class and its superclasses */
overrides = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
for (otype = type; otype != G_TYPE_OBJECT; otype = g_type_parent (otype)) {
type_overrides = g_type_get_qdata (otype, setting_property_overrides_quark);
if (type_overrides)
g_array_append_vals (overrides, (NMSettingProperty *)type_overrides->data, type_overrides->len);
}
/* Build the properties array from the GParamSpecs, obeying overrides */
properties = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
property_specs = g_object_class_list_properties (G_OBJECT_CLASS (setting_class),
&n_property_specs);
for (i = 0; i < n_property_specs; i++) {
override = find_property (overrides, property_specs[i]->name);
if (override)
property = *override;
else {
memset (&property, 0, sizeof (property));
property.name = property_specs[i]->name;
property.param_spec = property_specs[i];
}
g_array_append_val (properties, property);
}
g_free (property_specs);
/* Add any remaining overrides not corresponding to GObject properties */
for (i = 0; i < overrides->len; i++) {
override = &g_array_index (overrides, NMSettingProperty, i);
if (!g_object_class_find_property (G_OBJECT_CLASS (setting_class), override->name))
g_array_append_val (properties, *override);
}
g_array_unref (overrides);
g_type_set_qdata (type, setting_properties_quark, properties);
return properties;
}
static const NMSettingProperty *
nm_setting_class_get_properties (NMSettingClass *setting_class, guint *n_properties)
{
GArray *properties;
properties = nm_setting_class_ensure_properties (setting_class);
*n_properties = properties->len;
return (NMSettingProperty *) properties->data;
}
static const NMSettingProperty *
nm_setting_class_find_property (NMSettingClass *setting_class, const char *property_name)
{
GArray *properties;
properties = nm_setting_class_ensure_properties (setting_class);
return find_property (properties, property_name);
}
/*************************************************************/
static void
destroy_gvalue (gpointer data)
{
GValue *value = (GValue *) data;
g_value_unset (value);
if (G_IS_VALUE (value))
g_value_unset (value);
g_slice_free (GValue, value);
}
/**
* _nm_setting_to_dbus:
* @setting: the #NMSetting
* @connection: the #NMConnection containing @setting
* @flags: hash flags, e.g. %NM_CONNECTION_SERIALIZE_ALL
*
* Converts the #NMSetting into a #GHashTable mapping each setting property
......@@ -394,47 +569,59 @@ destroy_gvalue (gpointer data)
* describing the setting's properties
**/
GHashTable *
_nm_setting_to_dbus (NMSetting *setting, NMConnectionSerializationFlags flags)
_nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags)
{
GHashTable *hash;
GParamSpec **property_specs;
guint n_property_specs;
guint i;
const NMSettingProperty *properties;
guint n_properties, i;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
hash = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free, destroy_gvalue);
for (i = 0; i < n_property_specs; i++) {
GParamSpec *prop_spec = property_specs[i];
for (i = 0; i < n_properties; i++) {
const NMSettingProperty *property = &properties[i];
GParamSpec *prop_spec = property->param_spec;
GValue *value;
gboolean set;
if (!prop_spec && !property->get_func) {
/* Override property with no get_func, so we skip it. */
continue;
}
/* 'name' doesn't get serialized */
if (strcmp (g_param_spec_get_name (prop_spec), NM_SETTING_NAME) == 0)
if (prop_spec && !(prop_spec->flags & G_PARAM_WRITABLE))
continue;
if ( (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
&& (prop_spec->flags & NM_SETTING_PARAM_SECRET))
&& (prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
continue;
if ( (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
&& !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
&& !(prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
continue;
value = g_slice_new0 (GValue);
g_value_init (value, prop_spec->value_type);
g_object_get_property (G_OBJECT (setting), prop_spec->name, value);
if (property->get_func) {
g_value_init (value, property->dbus_type);
set = property->get_func (setting, connection, property->name, value);
} else if (prop_spec) {
g_value_init (value, prop_spec->value_type);
g_object_get_property (G_OBJECT (setting), prop_spec->name, value);
/* Don't serialize values with default values */
set = !g_param_value_defaults (prop_spec, value);
} else
g_assert_not_reached ();
/* Don't serialize values with default values */
if (!g_param_value_defaults (prop_spec, value))
g_hash_table_insert (hash, g_strdup (prop_spec->name), value);
if (set)
g_hash_table_insert (hash, g_strdup (property->name), value);
else
destroy_gvalue (value);
}
g_free (property_specs);
/* Don't return empty hashes, except for base types */
if (g_hash_table_size (hash) < 1 && !_nm_setting_is_base_type (setting)) {
......@@ -448,8 +635,11 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnectionSerializationFlags flags)
/**
* _nm_setting_new_from_dbus:
* @setting_type: the #NMSetting type which the hash contains properties for
* @hash: (element-type utf8 GObject.Value): the #GHashTable containing a
* string to GValue mapping of properties that apply to the setting
* @setting_hash: (element-type utf8 GObject.Value): the #GHashTable containing a
* string to #GValue mapping of properties that apply to the setting
* @connection_hash: (element-type utf8 GObject.Value): the #GHashTable containing a
* string to #GHashTable mapping of properties for the whole connection
* @error: location to store error, or %NULL
*
* Creates a new #NMSetting object and populates that object with the properties
* contained in the hash table, using each hash key as the property to set,
......@@ -462,18 +652,21 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnectionSerializationFlags flags)
* hash table, or %NULL on failure
**/
NMSetting *
_nm_setting_new_from_dbus (GType setting_type, GHashTable *hash)
_nm_setting_new_from_dbus (GType setting_type,
GHashTable *setting_hash,
GHashTable *connection_hash,
GError **error)
{
NMSetting *setting;
GObjectClass *class;
NMSettingClass *class;
GHashTableIter iter;
const char *prop_name;
GParamSpec **property_specs;
guint n_property_specs;
const NMSettingProperty *properties;
guint n_properties;
guint i;
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
g_return_val_if_fail (hash != NULL, NULL);
g_return_val_if_fail (setting_hash != NULL, NULL);
/* g_type_class_ref() ensures the setting class is created if it hasn't
* already been used.
......@@ -481,9 +674,9 @@ _nm_setting_new_from_dbus (GType setting_type, GHashTable *hash)
class = g_type_class_ref (setting_type);
/* Check for invalid properties first. */
g_hash_table_iter_init (&iter, hash);
g_hash_table_iter_init (&iter, setting_hash);
while (g_hash_table_iter_next (&iter, (gpointer) &prop_name, NULL)) {
if (!g_object_class_find_property (class, prop_name)) {
if (!nm_setting_class_find_property (class, prop_name)) {
/* Oh, we're so nice and only warn, maybe it should be a fatal error? */
g_warning ("Ignoring invalid property '%s'", prop_name);
continue;
......@@ -493,22 +686,32 @@ _nm_setting_new_from_dbus (GType setting_type, GHashTable *hash)
/* Now build the setting object from the legitimate properties */
setting = (NMSetting *) g_object_new (setting_type, NULL);
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];
GValue *value;
properties = nm_setting_class_get_properties (class, &n_properties);
for (i = 0; i < n_properties; i++) {
const NMSettingProperty *property = &properties[i];
GValue *value = g_hash_table_lookup (setting_hash, property->name);
/* 'name' doesn't get deserialized */
if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
continue;
value = g_hash_table_lookup (hash, prop_spec->name);
if (!value)
continue;
g_object_set_property (G_OBJECT (setting), prop_spec->name, value);
if (property->set_func) {
if (!property->set_func (setting,
connection_hash,
property->name,
value,
error)) {
g_object_unref (setting);
setting = NULL;
break;
}
} else if (property->param_spec) {
if (!(property->param_spec->flags & G_PARAM_WRITABLE))
continue;
g_object_set_property (G_OBJECT (setting), property->param_spec->name, value);
}
}
g_free (property_specs);
g_type_class_unref (class);
return setting;
......@@ -1065,10 +1268,11 @@ nm_setting_need_secrets (NMSetting *setting)
static int
update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
{
const NMSettingProperty *property;
GParamSpec *prop_spec;
prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
if (!prop_spec) {
property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), key);
if (!property) {
g_set_error (error,
NM_SETTING_ERROR,
NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
......@@ -1077,7 +1281,8 @@ update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **
}
/* Silently ignore non-secrets */
if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET))
prop_spec = property->param_spec;
if (!prop_spec || !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) {
......@@ -1154,10 +1359,11 @@ _nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **er
static gboolean
is_secret_prop (NMSetting *setting, const char *secret_name, GError **error)
{
const NMSettingProperty *property;
GParamSpec *pspec;
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), secret_name);
if (!pspec) {
property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), secret_name);
if (!property) {
g_set_error (error,
NM_SETTING_ERROR,
NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
......@@ -1165,7 +1371,8 @@ is_secret_prop (NMSetting *setting, const char *secret_name, GError **error)
return FALSE;
}
if (!(pspec->flags & NM_SETTING_PARAM_SECRET)) {
pspec = property->param_spec;
if (!pspec || !(pspec->flags & NM_SETTING_PARAM_SECRET)) {
g_set_error (error,
NM_SETTING_ERROR,
NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
......@@ -1507,6 +1714,11 @@ nm_setting_class_init (NMSettingClass *setting_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
if (!setting_property_overrides_quark)
setting_property_overrides_quark = g_quark_from_static_string ("nm-setting-property-overrides");
if (!setting_properties_quark)
setting_properties_quark = g_quark_from_static_string ("nm-setting-properties");
g_type_class_add_private (setting_class, sizeof (NMSettingPrivate));
/* virtual methods */
......
......@@ -88,7 +88,9 @@ nm_simple_connection_new_from_dbus (GHashTable *hash, GError **error)
goto failed;
}
setting = _nm_setting_new_from_dbus (type, setting_hash);
setting = _nm_setting_new_from_dbus (type, setting_hash, hash, error);
if (!setting)
goto failed;
nm_connection_add_setting (connection, setting);
}
......
......@@ -680,7 +680,7 @@ test_setting_to_dbus_all (void)
s_wsec = make_test_wsec_setting ("setting-to-dbus-all");
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NM_CONNECTION_SERIALIZE_ALL);
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NULL, NM_CONNECTION_SERIALIZE_ALL);
/* Make sure all keys are there */
ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT),
......@@ -704,7 +704,7 @@ test_setting_to_dbus_no_secrets (void)
s_wsec = make_test_wsec_setting ("setting-to-dbus-no-secrets");
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NM_CONNECTION_SERIALIZE_NO_SECRETS);
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NULL, NM_CONNECTION_SERIALIZE_NO_SECRETS);
/* Make sure non-secret keys are there */
ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT),
......@@ -730,7 +730,7 @@ test_setting_to_dbus_only_secrets (void)
s_wsec = make_test_wsec_setting ("setting-to-dbus-only-secrets");
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NULL, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
/* Make sure non-secret keys are there */
ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT) == NULL,
......@@ -778,10 +778,10 @@ test_setting_new_from_dbus (void)
GHashTable *hash;
s_wsec = make_test_wsec_setting ("setting-to-dbus-all");
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NM_CONNECTION_SERIALIZE_ALL);
hash = _nm_setting_to_dbus (NM_SETTING (s_wsec), NULL, NM_CONNECTION_SERIALIZE_ALL);
g_object_unref (s_wsec);
s_wsec = (NMSettingWirelessSecurity *) _nm_setting_new_from_dbus (NM_TYPE_SETTING_WIRELESS_SECURITY, hash);
s_wsec = (NMSettingWirelessSecurity *) _nm_setting_new_from_dbus (NM_TYPE_SETTING_WIRELESS_SECURITY, hash, NULL, NULL);
g_hash_table_destroy (hash);
g_assert (s_wsec);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment