NetworkManager/libnm-core/nm-setting.c
Thomas Haller a5b20ba211 libnm-core: add _nm_setting_secret_flags_valid() helper
Secret-flags are flags, but most combinations don't actually make sense
and maybe should be rejected. Anyway, that is not done, and most places
just check that there are no unknown flags set.

Add _nm_setting_secret_flags_valid() to perform the check at one place
instead of having the implementation at various places.
2019-01-07 11:20:56 +01:00

2560 lines
84 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* Copyright 2007 - 2011 Red Hat, Inc.
* Copyright 2007 - 2008 Novell, Inc.
*/
#include "nm-default.h"
#include "nm-setting.h"
#include <string.h>
#include "nm-setting-private.h"
#include "nm-utils.h"
#include "nm-core-internal.h"
#include "nm-utils-private.h"
#include "nm-property-compare.h"
#include "nm-setting-connection.h"
#include "nm-setting-bond.h"
#include "nm-setting-bridge.h"
#include "nm-setting-bridge-port.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-team.h"
#include "nm-setting-team-port.h"
#include "nm-setting-vpn.h"
/**
* SECTION:nm-setting
* @short_description: Describes related configuration information
*
* Each #NMSetting contains properties that describe configuration that applies
* to a specific network layer (like IPv4 or IPv6 configuration) or device type
* (like Ethernet, or Wi-Fi). A collection of individual settings together
* make up an #NMConnection. Each property is strongly typed and usually has
* a number of allowed values. See each #NMSetting subclass for a description
* of properties and allowed values.
*/
/*****************************************************************************/
typedef struct {
GHashTable *hash;
const char **names;
GVariant **values;
} GenData;
typedef struct {
const char *name;
GType type;
NMSettingPriority priority;
} SettingInfo;
enum {
PROP_0,
PROP_NAME,
PROP_LAST
};
typedef struct {
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))
/*****************************************************************************/
static GenData *_gendata_hash (NMSetting *setting, gboolean create_if_necessary);
/*****************************************************************************/
static NMSettingPriority
_get_base_type_priority (const NMMetaSettingInfo *setting_info,
GType gtype)
{
/* Historical oddity: PPPoE is a base-type even though it's not
* priority 1. It needs to be sorted *after* lower-level stuff like
* Wi-Fi security or 802.1x for secrets, but it's still allowed as a
* base type.
*/
if (setting_info) {
if ( NM_IN_SET (setting_info->setting_priority,
NM_SETTING_PRIORITY_HW_BASE,
NM_SETTING_PRIORITY_HW_NON_BASE)
|| gtype == NM_TYPE_SETTING_PPPOE)
return setting_info->setting_priority;
}
return NM_SETTING_PRIORITY_INVALID;
}
NMSettingPriority
_nm_setting_get_setting_priority (NMSetting *setting)
{
const NMMetaSettingInfo *setting_info;
g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);
setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
return setting_info ? setting_info->setting_priority : NM_SETTING_PRIORITY_INVALID;
}
NMSettingPriority
_nm_setting_type_get_base_type_priority (GType type)
{
return _get_base_type_priority (nm_meta_setting_infos_by_gtype (type),
type);
}
NMSettingPriority
_nm_setting_get_base_type_priority (NMSetting *setting)
{
g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);
return _get_base_type_priority (NM_SETTING_GET_CLASS (setting)->setting_info,
G_OBJECT_TYPE (setting));
}
/**
* nm_setting_lookup_type:
* @name: a setting name
*
* Returns the #GType of the setting's class for a given setting name.
*
* Returns: the #GType of the setting's class, or %G_TYPE_INVALID if
* @name is not recognized.
**/
GType
nm_setting_lookup_type (const char *name)
{
const NMMetaSettingInfo *setting_info;
g_return_val_if_fail (name, G_TYPE_INVALID);
setting_info = nm_meta_setting_infos_by_name (name);
return setting_info ? setting_info->get_setting_gtype () : G_TYPE_INVALID;
}
int
_nm_setting_compare_priority (gconstpointer a, gconstpointer b)
{
NMSettingPriority prio_a, prio_b;
prio_a = _nm_setting_get_setting_priority ((NMSetting *) a);
prio_b = _nm_setting_get_setting_priority ((NMSetting *) b);
if (prio_a < prio_b)
return -1;
else if (prio_a == prio_b)
return 0;
return 1;
}
/*****************************************************************************/
gboolean
_nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_type)
{
const char *port_type = NULL;
gboolean found = TRUE;
if (!slave_type)
found = FALSE;
else if (!strcmp (slave_type, NM_SETTING_BOND_SETTING_NAME))
;
else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME))
port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME;
else if (!strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
port_type = NM_SETTING_OVS_PORT_SETTING_NAME;
else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
port_type = NM_SETTING_TEAM_PORT_SETTING_NAME;
else
found = FALSE;
if (out_port_type)
*out_port_type = port_type;
return found;
}
/*****************************************************************************/
static const NMSettInfoProperty *
_nm_sett_info_property_find_in_array (const NMSettInfoProperty *properties, guint len, const char *name)
{
guint i;
for (i = 0; i < len; i++) {
if (nm_streq (name, properties[i].name))
return &properties[i];
}
return NULL;
}
void
_properties_override_add_struct (GArray *properties_override,
const NMSettInfoProperty *prop_info)
{
nm_assert (properties_override);
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 (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
properties_override->len,
prop_info->name ?: prop_info->param_spec->name));
nm_assert (!prop_info->from_dbus || prop_info->dbus_type);
nm_assert (!prop_info->set_func || prop_info->dbus_type);
g_array_append_vals (properties_override, prop_info, 1);
if (!prop_info->name) {
/* for convenience, allow omitting "name" if "param_spec" is given. */
g_array_index (properties_override,
NMSettInfoProperty,
properties_override->len - 1).name = prop_info->param_spec->name;
}
}
/**
* _properties_override_add_dbus_only:
* @properties_override: an array collecting the overrides
* @property_name: the name of the property to override
* @dbus_type: the type of the property (in its D-Bus representation)
* @synth_func: (allow-none): function to call to synthesize a value for 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 this setting type, but which does not correspond to
* a #GObject property.
*
* When serializing a setting to D-Bus, @synth_func will be called to synthesize
* a value for the property. (If it returns %NULL, no value will be added to the
* serialization. If @synth_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 it. (If @set_func is %NULL
* then the property will be ignored when deserializing.)
*/
void
_properties_override_add_dbus_only (GArray *properties_override,
const char *property_name,
const GVariantType *dbus_type,
NMSettingPropertySynthFunc synth_func,
NMSettingPropertySetFunc set_func)
{
_properties_override_add (properties_override,
.name = property_name,
.dbus_type = dbus_type,
.synth_func = synth_func,
.set_func = set_func);
}
/**
* _properties_override_add_override:
* @properties_override: an array collecting the overrides
* @param_spec: the name of the property to override
* @dbus_type: the type of the property (in its D-Bus representation)
* @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
* @not_set_func: (allow-none): function to call to indicate the property was not set
*
* Overrides the D-Bus representation of the #GObject property that shares the
* same name as @param_spec.
*
* When serializing a setting to D-Bus, if @get_func is non-%NULL, then it will
* be called to get the property's value. If it returns a #GVariant, the
* property will be added to the hash, and if it returns %NULL, the property
* will be omitted. (If @get_func is %NULL, the property will be read normally
* with g_object_get_property(), and added to the hash if it is not the default
* value.)
*
* When deserializing a D-Bus representation into a setting, if a value with
* the name of @param_spec is present, then @set_func will be called to set it.
* (If @set_func is %NULL then the property will be set normally with
* g_object_set_property().)
*
* If @not_set_func is non-%NULL, then it will be called when deserializing a
* representation that does NOT contain a value for the property. This can be used,
* eg, if a new property needs to be initialized from some older deprecated property
* when it is not present.
*/
void
_properties_override_add_override (GArray *properties_override,
GParamSpec *param_spec,
const GVariantType *dbus_type,
NMSettingPropertyGetFunc get_func,
NMSettingPropertySetFunc set_func,
NMSettingPropertyNotSetFunc not_set_func)
{
nm_assert (param_spec);
_properties_override_add (properties_override,
.param_spec = param_spec,
.dbus_type = dbus_type,
.get_func = get_func,
.set_func = set_func,
.not_set_func = not_set_func);
}
/**
* _properties_override_add_transform:
* @properties_override: an array collecting the overrides
* @param_spec: the param spec of the property to transform.
* @dbus_type: the type of the property (in its D-Bus representation)
* @to_dbus: function to convert from object to D-Bus format
* @from_dbus: function to convert from D-Bus to object format
*
* Indicates that @property on @setting_class does not have the same format as
* its corresponding D-Bus representation, and so must be transformed when
* serializing/deserializing.
*
* The transformation will also be used by nm_setting_compare(), meaning that
* the underlying object property does not need to be of a type that
* nm_property_compare() recognizes, as long as it recognizes @dbus_type.
*/
void
_properties_override_add_transform (GArray *properties_override,
GParamSpec *param_spec,
const GVariantType *dbus_type,
NMSettingPropertyTransformToFunc to_dbus,
NMSettingPropertyTransformFromFunc from_dbus)
{
nm_assert (param_spec);
_properties_override_add (properties_override,
.param_spec = param_spec,
.dbus_type = dbus_type,
.to_dbus = to_dbus,
.from_dbus = from_dbus);
}
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,
const NMSettInfoSettDetail *detail,
GArray *properties_override)
{
NMSettInfoSetting *sett_info;
gs_free GParamSpec **property_specs = NULL;
guint i, n_property_specs, override_len;
nm_assert (NM_IS_SETTING_CLASS (setting_class));
nm_assert (!setting_class->setting_info);
nm_assert (meta_type < G_N_ELEMENTS (_sett_info_settings));
sett_info = &_sett_info_settings[meta_type];
nm_assert (!sett_info->setting_class);
nm_assert (!sett_info->property_infos_len);
nm_assert (!sett_info->property_infos);
if (!properties_override) {
override_len = 0;
properties_override = _nm_sett_info_property_override_create_array ();
} else
override_len = properties_override->len;
property_specs = g_object_class_list_properties (G_OBJECT_CLASS (setting_class),
&n_property_specs);
#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);
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))
continue;
nm_assert (!found);
found = TRUE;
nm_assert (p->param_spec == property_specs[j]);
}
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 ((NMSettInfoProperty *) properties_override->data,
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);
memset (p, 0, sizeof (*p));
p->name = name;
p->param_spec = property_specs[i];
}
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 (detail)
sett_info->detail = *detail;
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 *
_nm_sett_info_setting_get (NMSettingClass *setting_class)
{
if ( NM_IS_SETTING_CLASS (setting_class)
&& setting_class->setting_info) {
nm_assert (setting_class->setting_info->meta_type < G_N_ELEMENTS (_sett_info_settings));
return &_sett_info_settings[setting_class->setting_info->meta_type];
}
return NULL;
}
const NMSettInfoProperty *
_nm_sett_info_property_get (NMSettingClass *setting_class,
const char *property_name)
{
const NMSettInfoSetting *sett_info = _nm_sett_info_setting_get (setting_class);
const NMSettInfoProperty *property;
gssize idx;
if (!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);
if (idx < 0)
return NULL;
property = &sett_info->property_infos[idx];
nm_assert (idx == 0 || strcmp (property[-1].name, property[0].name) < 0);
nm_assert (idx == sett_info->property_infos_len - 1 || strcmp (property[0].name, property[1].name) < 0);
return property;
}
/*****************************************************************************/
gboolean
_nm_setting_use_legacy_property (NMSetting *setting,
GVariant *connection_dict,
const char *legacy_property,
const char *new_property)
{
GVariant *setting_dict, *value;
setting_dict = g_variant_lookup_value (connection_dict, nm_setting_get_name (NM_SETTING (setting)), NM_VARIANT_TYPE_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) {
g_variant_unref (setting_dict);
return TRUE;
}
g_variant_unref (value);
/* Otherwise, clients always prefer new properties sent from the daemon. */
if (!_nm_utils_is_manager_process) {
g_variant_unref (setting_dict);
return FALSE;
}
/* The daemon prefers the legacy property if it exists. */
value = g_variant_lookup_value (setting_dict, legacy_property, NULL);
g_variant_unref (setting_dict);
if (value) {
g_variant_unref (value);
return TRUE;
} else
return FALSE;
}
/*****************************************************************************/
static const GVariantType *
variant_type_for_gtype (GType type)
{
if (type == G_TYPE_BOOLEAN)
return G_VARIANT_TYPE_BOOLEAN;
else if (type == G_TYPE_UCHAR)
return G_VARIANT_TYPE_BYTE;
else if (type == G_TYPE_INT)
return G_VARIANT_TYPE_INT32;
else if (type == G_TYPE_UINT)
return G_VARIANT_TYPE_UINT32;
else if (type == G_TYPE_INT64)
return G_VARIANT_TYPE_INT64;
else if (type == G_TYPE_UINT64)
return G_VARIANT_TYPE_UINT64;
else if (type == G_TYPE_STRING)
return G_VARIANT_TYPE_STRING;
else if (type == G_TYPE_DOUBLE)
return G_VARIANT_TYPE_DOUBLE;
else if (type == G_TYPE_STRV)
return G_VARIANT_TYPE_STRING_ARRAY;
else if (type == G_TYPE_BYTES)
return G_VARIANT_TYPE_BYTESTRING;
else if (g_type_is_a (type, G_TYPE_ENUM))
return G_VARIANT_TYPE_INT32;
else if (g_type_is_a (type, G_TYPE_FLAGS))
return G_VARIANT_TYPE_UINT32;
else
g_assert_not_reached ();
}
static GVariant *
get_property_for_dbus (NMSetting *setting,
const NMSettInfoProperty *property,
gboolean ignore_default)
{
GValue prop_value = { 0, };
GVariant *dbus_value;
if (property->get_func)
return property->get_func (setting, property->name);
else
g_return_val_if_fail (property->param_spec != NULL, NULL);
g_value_init (&prop_value, property->param_spec->value_type);
g_object_get_property (G_OBJECT (setting), property->param_spec->name, &prop_value);
if (ignore_default && g_param_value_defaults (property->param_spec, &prop_value)) {
g_value_unset (&prop_value);
return NULL;
}
if (property->to_dbus)
dbus_value = property->to_dbus (&prop_value);
else if (property->dbus_type)
dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, property->dbus_type);
else if (g_type_is_a (prop_value.g_type, G_TYPE_ENUM))
dbus_value = g_variant_new_int32 (g_value_get_enum (&prop_value));
else if (g_type_is_a (prop_value.g_type, G_TYPE_FLAGS))
dbus_value = g_variant_new_uint32 (g_value_get_flags (&prop_value));
else if (prop_value.g_type == G_TYPE_BYTES)
dbus_value = nm_utils_gbytes_to_variant_ay (g_value_get_boxed (&prop_value));
else
dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, variant_type_for_gtype (prop_value.g_type));
g_value_unset (&prop_value);
return dbus_value;
}
static gboolean
set_property_from_dbus (const NMSettInfoProperty *property,
GVariant *src_value,
GValue *dst_value)
{
g_return_val_if_fail (property->param_spec != NULL, FALSE);
if (property->from_dbus) {
if (!g_variant_type_equal (g_variant_get_type (src_value), property->dbus_type))
return FALSE;
property->from_dbus (src_value, dst_value);
} else if (dst_value->g_type == G_TYPE_BYTES) {
if (!g_variant_is_of_type (src_value, G_VARIANT_TYPE_BYTESTRING))
return FALSE;
_nm_utils_bytes_from_dbus (src_value, dst_value);
} else {
GValue tmp = G_VALUE_INIT;
g_dbus_gvariant_to_gvalue (src_value, &tmp);
if (G_VALUE_TYPE (&tmp) == G_VALUE_TYPE (dst_value))
*dst_value = tmp;
else {
gboolean success;
success = g_value_transform (&tmp, dst_value);
g_value_unset (&tmp);
if (!success)
return FALSE;
}
}
return TRUE;
}
/**
* _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 #GVariant of type #NM_VARIANT_TYPE_SETTING
* mapping each setting property name to a value describing that property,
* suitable for marshalling over D-Bus or serializing.
*
* Returns: (transfer none): a new floating #GVariant describing the setting's
* properties
**/
GVariant *
_nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags)
{
NMSettingPrivate *priv;
GVariantBuilder builder;
GVariant *dbus_value;
const NMSettInfoSetting *sett_info;
guint n_properties, i;
const char *const*gendata_keys;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
priv = NM_SETTING_GET_PRIVATE (setting);
g_variant_builder_init (&builder, NM_VARIANT_TYPE_SETTING);
n_properties = _nm_setting_gendata_get_all (setting, &gendata_keys, NULL);
for (i = 0; i < n_properties; i++) {
g_variant_builder_add (&builder,
"{sv}",
gendata_keys[i],
g_hash_table_lookup (priv->gendata->hash, gendata_keys[i]));
}
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property = &sett_info->property_infos[i];
GParamSpec *prop_spec = property->param_spec;
if (!prop_spec) {
if (!property->synth_func)
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;
if (NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_GENDATA_BACKED))
continue;
if ( (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
&& !_nm_utils_is_manager_process)
continue;
if ( (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
&& (prop_spec->flags & NM_SETTING_PARAM_SECRET))
continue;
if ( (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
&& !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
continue;
}
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. */
g_variant_take_ref (dbus_value);
g_variant_builder_add (&builder, "{sv}", property->name, dbus_value);
g_variant_unref (dbus_value);
}
}
return g_variant_builder_end (&builder);
}
/**
* _nm_setting_new_from_dbus:
* @setting_type: the #NMSetting type which the hash contains properties for
* @setting_dict: the #GVariant containing an %NM_VARIANT_TYPE_SETTING dictionary
* mapping property names to values
* @connection_dict: the #GVariant containing an %NM_VARIANT_TYPE_CONNECTION
* dictionary mapping setting names to dictionaries.
* @parse_flags: flags to determine behavior during parsing.
* @error: location to store error, or %NULL
*
* Creates a new #NMSetting object and populates that object with the properties
* contained in @setting_dict, using each key as the property to set, and each
* value as the value to set that property to. Setting properties are strongly
* typed, thus the #GVariantType of the dict value must be correct. See the
* documentation on each #NMSetting object subclass for the correct property
* names and value types.
*
* Returns: a new #NMSetting object populated with the properties from the
* hash table, or %NULL if @setting_hash could not be deserialized.
**/
NMSetting *
_nm_setting_new_from_dbus (GType setting_type,
GVariant *setting_dict,
GVariant *connection_dict,
NMSettingParseFlags parse_flags,
GError **error)
{
gs_unref_object NMSetting *setting = NULL;
gs_unref_hashtable GHashTable *keys = NULL;
const NMSettInfoSetting *sett_info;
guint i;
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
g_return_val_if_fail (g_variant_is_of_type (setting_dict, NM_VARIANT_TYPE_SETTING), NULL);
nm_assert (!NM_FLAGS_ANY (parse_flags, ~NM_SETTING_PARSE_FLAGS_ALL));
nm_assert (!NM_FLAGS_ALL (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_BEST_EFFORT));
/* connection_dict is not technically optional, but some tests in test-general
* don't bother with it in cases where they know it's not needed.
*/
if (connection_dict)
g_return_val_if_fail (g_variant_is_of_type (connection_dict, NM_VARIANT_TYPE_CONNECTION), NULL);
/* Build the setting object from the properties we know about; we assume
* that any propreties in @setting_dict that we don't know about can
* either be ignored or else has a backward-compatibility equivalent
* that we do know about.
*/
setting = (NMSetting *) g_object_new (setting_type, NULL);
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
GVariantIter iter;
GVariant *entry, *entry_key;
char *key;
keys = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
g_variant_iter_init (&iter, setting_dict);
while ((entry = g_variant_iter_next_value (&iter))) {
entry_key = g_variant_get_child_value (entry, 0);
key = g_strdup (g_variant_get_string (entry_key, NULL));
g_variant_unref (entry_key);
g_variant_unref (entry);
if (!g_hash_table_add (keys, key)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
_("duplicate property"));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
return NULL;
}
}
}
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
if (sett_info->detail.gendata_info) {
GHashTable *hash;
GVariantIter iter;
char *key;
GVariant *val;
hash = _gendata_hash (setting, TRUE)->hash;
g_variant_iter_init (&iter, setting_dict);
while (g_variant_iter_next (&iter, "{sv}", &key, &val)) {
g_hash_table_insert (hash,
key,
val);
}
_nm_setting_gendata_notify (setting, TRUE);
return g_steal_pointer (&setting);
}
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property = &sett_info->property_infos[i];
gs_unref_variant GVariant *value = NULL;
gs_free_error GError *local = NULL;
if (property->param_spec && !(property->param_spec->flags & G_PARAM_WRITABLE))
continue;
value = g_variant_lookup_value (setting_dict, property->name, NULL);
if (value && keys)
g_hash_table_remove (keys, property->name);
if (value && property->set_func) {
if (!g_variant_type_equal (g_variant_get_type (value), property->dbus_type)) {
/* for backward behavior, fail unless best-effort is chosen. */
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
continue;
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("can't set property of type '%s' from value of type '%s'"),
property->dbus_type ?
g_variant_type_peek_string (property->dbus_type) :
property->param_spec ?
g_type_name (property->param_spec->value_type) : "(unknown)",
g_variant_get_type_string (value));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
return NULL;
}
if (!property->set_func (setting,
connection_dict,
property->name,
value,
parse_flags,
&local)) {
if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
continue;
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("failed to set property: %s"),
local->message);
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
return NULL;
}
} else if (!value && property->not_set_func) {
if (!property->not_set_func (setting,
connection_dict,
property->name,
parse_flags,
&local)) {
if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
continue;
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("failed to set property: %s"),
local->message);
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
return NULL;
}
} else if (value && property->param_spec) {
nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
g_value_init (&object_value, property->param_spec->value_type);
if (!set_property_from_dbus (property, value, &object_value)) {
/* for backward behavior, fail unless best-effort is chosen. */
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
continue;
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("can't set property of type '%s' from value of type '%s'"),
property->dbus_type ?
g_variant_type_peek_string (property->dbus_type) :
property->param_spec ?
g_type_name (property->param_spec->value_type) : "(unknown)",
g_variant_get_type_string (value));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
return NULL;
}
if (!nm_g_object_set_property (G_OBJECT (setting), property->param_spec->name, &object_value, &local)) {
if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
continue;
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("can not set property: %s"),
local->message);
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
return NULL;
}
}
}
if ( NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)
&& g_hash_table_size (keys) > 0) {
GHashTableIter iter;
const char *key;
g_hash_table_iter_init (&iter, keys);
if (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("unknown property"));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
return NULL;
}
}
return g_steal_pointer (&setting);
}
/**
* nm_setting_get_dbus_property_type:
* @setting: an #NMSetting
* @property_name: the property of @setting to get the type of
*
* Gets the D-Bus marshalling type of a property. @property_name is a D-Bus
* property name, which may not necessarily be a #GObject property.
*
* Returns: the D-Bus marshalling type of @property on @setting.
*/
const GVariantType *
nm_setting_get_dbus_property_type (NMSetting *setting,
const char *property_name)
{
const NMSettInfoProperty *property;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
g_return_val_if_fail (property_name != NULL, NULL);
property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), property_name);
g_return_val_if_fail (property != NULL, NULL);
if (property->dbus_type)
return property->dbus_type;
else
return variant_type_for_gtype (property->param_spec->value_type);
}
gboolean
_nm_setting_get_property (NMSetting *setting, const char *property_name, GValue *value)
{
const NMSettInfoSetting *sett_info;
GParamSpec *prop_spec;
g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
g_return_val_if_fail (property_name, FALSE);
g_return_val_if_fail (value, FALSE);
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
if (sett_info->detail.gendata_info) {
GVariant *variant;
GenData *gendata = _gendata_hash (setting, FALSE);
variant = gendata ? g_hash_table_lookup (gendata->hash, property_name) : NULL;
if (!variant) {
g_value_unset (value);
return FALSE;
}
g_value_init (value, G_TYPE_VARIANT);
g_value_set_variant (value, variant);
return TRUE;
}
prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
if (!prop_spec) {
g_value_unset (value);
return FALSE;
}
g_value_init (value, prop_spec->value_type);
g_object_get_property (G_OBJECT (setting), property_name, value);
return TRUE;
}
static void
_gobject_copy_property (GObject *src,
GObject *dst,
const char *property_name,
GType gtype)
{
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);
}
/**
* nm_setting_duplicate:
* @setting: the #NMSetting to duplicate
*
* Duplicates a #NMSetting.
*
* Returns: (transfer full): a new #NMSetting containing the same properties and values as the
* source #NMSetting
**/
NMSetting *
nm_setting_duplicate (NMSetting *setting)
{
const NMSettInfoSetting *sett_info;
GObject *dup;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
dup = g_object_new (G_OBJECT_TYPE (setting), NULL);
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
if (sett_info->detail.gendata_info) {
GenData *gendata = _gendata_hash (setting, FALSE);
if ( gendata
&& g_hash_table_size (gendata->hash) > 0) {
GHashTableIter iter;
GHashTable *h = _gendata_hash (NM_SETTING (dup), TRUE)->hash;
const char *key;
GVariant *val;
g_hash_table_iter_init (&iter, gendata->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
g_hash_table_insert (h,
g_strdup (key),
g_variant_ref (val));
}
}
}
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);
}
/**
* nm_setting_get_name:
* @setting: the #NMSetting
*
* Returns the type name of the #NMSetting object
*
* Returns: a string containing the type name of the #NMSetting object,
* like 'ppp' or 'wireless' or 'wired'.
**/
const char *
nm_setting_get_name (NMSetting *setting)
{
const NMMetaSettingInfo *setting_info;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
return setting_info ? setting_info->setting_name : NULL;
}
/**
* nm_setting_verify:
* @setting: the #NMSetting to verify
* @connection: (allow-none): the #NMConnection that @setting came from, or
* %NULL if @setting is being verified in isolation.
* @error: location to store error, or %NULL
*
* Validates the setting. Each setting's properties have allowed values, and
* some are dependent on other values (hence the need for @connection). The
* returned #GError contains information about which property of the setting
* failed validation, and in what way that property failed validation.
*
* Returns: %TRUE if the setting is valid, %FALSE if it is not
**/
gboolean
nm_setting_verify (NMSetting *setting, NMConnection *connection, GError **error)
{
NMSettingVerifyResult result = _nm_setting_verify (setting, connection, error);
if (result == NM_SETTING_VERIFY_NORMALIZABLE)
g_clear_error (error);
return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE;
}
NMSettingVerifyResult
_nm_setting_verify (NMSetting *setting, NMConnection *connection, GError **error)
{
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);
return NM_SETTING_VERIFY_SUCCESS;
}
/**
* nm_setting_verify_secrets:
* @setting: the #NMSetting to verify secrets in
* @connection: (allow-none): the #NMConnection that @setting came from, or
* %NULL if @setting is being verified in isolation.
* @error: location to store error, or %NULL
*
* Verifies the secrets in the setting.
* The returned #GError contains information about which secret of the setting
* failed validation, and in what way that secret failed validation.
* The secret validation is done separately from main setting validation, because
* in some cases connection failure is not desired just for the secrets.
*
* Returns: %TRUE if the setting secrets are valid, %FALSE if they are not
*
* Since: 1.2
**/
gboolean
nm_setting_verify_secrets (NMSetting *setting, NMConnection *connection, GError **error)
{
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_secrets)
return NM_SETTING_GET_CLASS (setting)->verify_secrets (setting, connection, error);
return NM_SETTING_VERIFY_SUCCESS;
}
gboolean
_nm_setting_verify_secret_string (const char *str,
const char *setting_name,
const char *property,
GError **error)
{
if (str && !*str) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("property is empty"));
g_prefix_error (error, "%s.%s: ", setting_name, property);
return FALSE;
}
return TRUE;
}
static gboolean
compare_property (NMSetting *setting,
NMSetting *other,
const GParamSpec *prop_spec,
NMSettingCompareFlags flags)
{
const NMSettInfoProperty *property;
GVariant *value1, *value2;
int cmp;
/* Handle compare flags */
if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
g_return_val_if_fail (!NM_IS_SETTING_VPN (setting), FALSE);
if (!nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL))
g_return_val_if_reached (FALSE);
if (!nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL))
g_return_val_if_reached (FALSE);
/* If the secret flags aren't the same the settings aren't the same */
if (a_secret_flags != b_secret_flags)
return FALSE;
/* Check for various secret flags that might cause us to ignore comparing
* this property.
*/
if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
&& (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
return TRUE;
if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
&& (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
return TRUE;
}
property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), prop_spec->name);
g_return_val_if_fail (property != NULL, FALSE);
value1 = get_property_for_dbus (setting, property, TRUE);
value2 = get_property_for_dbus (other, property, TRUE);
cmp = nm_property_compare (value1, value2);
if (value1)
g_variant_unref (value1);
if (value2)
g_variant_unref (value2);
return cmp == 0;
}
/**
* nm_setting_compare:
* @a: a #NMSetting
* @b: a second #NMSetting to compare with the first
* @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
*
* Compares two #NMSetting objects for similarity, with comparison behavior
* modified by a set of flags. See the documentation for #NMSettingCompareFlags
* for a description of each flag's behavior.
*
* Returns: %TRUE if the comparison succeeds, %FALSE if it does not
**/
gboolean
nm_setting_compare (NMSetting *a,
NMSetting *b,
NMSettingCompareFlags flags)
{
const NMSettInfoSetting *sett_info;
guint i;
g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
/* First check that both have the same type */
if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
return FALSE;
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
if (sett_info->detail.gendata_info) {
GenData *a_gendata = _gendata_hash (a, FALSE);
GenData *b_gendata = _gendata_hash (b, FALSE);
return nm_utils_hash_table_equal (a_gendata ? a_gendata->hash : NULL,
b_gendata ? b_gendata->hash : NULL,
TRUE,
g_variant_equal);
}
/* And now all properties */
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)
&& !NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))
continue;
if ( NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
&& !NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_INFERRABLE))
continue;
if ( NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)
&& NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
continue;
if ( NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
&& NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET))
continue;
if (!NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags))
return FALSE;
}
return TRUE;
}
static inline gboolean
should_compare_prop (NMSetting *setting,
const char *prop_name,
NMSettingCompareFlags comp_flags,
GParamFlags prop_flags)
{
/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
if ( (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY)
&& (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
return FALSE;
if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
return FALSE;
if ((comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY) && !(prop_flags & NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
return FALSE;
if (prop_flags & NM_SETTING_PARAM_SECRET) {
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
return FALSE;
if ( NM_IS_SETTING_VPN (setting)
&& g_strcmp0 (prop_name, NM_SETTING_VPN_SECRETS) == 0) {
/* FIXME: NMSettingVPN:NM_SETTING_VPN_SECRETS has NM_SETTING_PARAM_SECRET.
* nm_setting_get_secret_flags() quite possibly fails, but it might succeed if the
* setting accidentally uses a key "secrets". */
return TRUE;
}
if (!nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL))
g_return_val_if_reached (FALSE);
if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
&& (secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
return FALSE;
if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
&& (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
return FALSE;
}
if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
&& NM_IS_SETTING_CONNECTION (setting)
&& !strcmp (prop_name, NM_SETTING_CONNECTION_ID))
return FALSE;
if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)
&& NM_IS_SETTING_CONNECTION (setting)
&& !strcmp (prop_name, NM_SETTING_CONNECTION_TIMESTAMP))
return FALSE;
return TRUE;
}
static void
_setting_diff_add_result (GHashTable *results, const char *prop_name, NMSettingDiffResult r)
{
void *p;
if (r == NM_SETTING_DIFF_RESULT_UNKNOWN)
return;
if (g_hash_table_lookup_extended (results, prop_name, NULL, &p)) {
if (!NM_FLAGS_ALL ((guint) r, GPOINTER_TO_UINT (p)))
g_hash_table_insert (results, g_strdup (prop_name), GUINT_TO_POINTER (((guint) r) | GPOINTER_TO_UINT (p)));
} else
g_hash_table_insert (results, g_strdup (prop_name), GUINT_TO_POINTER (r));
}
/**
* nm_setting_diff:
* @a: a #NMSetting
* @b: a second #NMSetting to compare with the first
* @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
* @invert_results: this parameter is used internally by libnm and should
* be set to %FALSE. If %TRUE inverts the meaning of the #NMSettingDiffResult.
* @results: (inout) (transfer full) (element-type utf8 guint32): if the
* settings differ, on return a hash table mapping the differing keys to one or
* more %NMSettingDiffResult values OR-ed together. If the settings do not
* differ, any hash table passed in is unmodified. If no hash table is passed
* in and the settings differ, a new one is created and returned.
*
* Compares two #NMSetting objects for similarity, with comparison behavior
* modified by a set of flags. See the documentation for #NMSettingCompareFlags
* for a description of each flag's behavior. If the settings differ, the keys
* of each setting that differ from the other are added to @results, mapped to
* one or more #NMSettingDiffResult values.
*
* Returns: %TRUE if the settings contain the same values, %FALSE if they do not
**/
gboolean
nm_setting_diff (NMSetting *a,
NMSetting *b,
NMSettingCompareFlags flags,
gboolean invert_results,
GHashTable **results)
{
const NMSettInfoSetting *sett_info;
guint i;
NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
NMSettingDiffResult a_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
NMSettingDiffResult b_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
gboolean results_created = FALSE;
gboolean compared_any = FALSE;
gboolean diff_found = FALSE;
g_return_val_if_fail (results != NULL, FALSE);
g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
if (b) {
g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
g_return_val_if_fail (G_OBJECT_TYPE (a) == G_OBJECT_TYPE (b), FALSE);
}
if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) ==
(NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) {
/* conflicting flags: default to WITH_DEFAULT (clearing NO_DEFAULT). */
flags &= ~NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT;
}
/* If the caller is calling this function in a pattern like this to get
* complete diffs:
*
* nm_setting_diff (A, B, FALSE, &results);
* nm_setting_diff (B, A, TRUE, &results);
*
* and wants us to invert the results so that the second invocation comes
* out correctly, do that here.
*/
if (invert_results) {
a_result = NM_SETTING_DIFF_RESULT_IN_B;
b_result = NM_SETTING_DIFF_RESULT_IN_A;
a_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
b_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
}
if (*results == NULL) {
*results = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
results_created = TRUE;
}
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
if (sett_info->detail.gendata_info) {
const char *key;
GVariant *val, *val2;
GHashTableIter iter;
GenData *a_gendata = _gendata_hash (a, FALSE);
GenData *b_gendata = b ? _gendata_hash (b, FALSE) : NULL;
if (!a_gendata || !b_gendata) {
if (a_gendata || b_gendata) {
NMSettingDiffResult one_sided_result;
one_sided_result = a_gendata ? a_result : b_result;
g_hash_table_iter_init (&iter, a_gendata ? a_gendata->hash : b_gendata->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
diff_found = TRUE;
_setting_diff_add_result (*results, key, one_sided_result);
}
}
} else {
g_hash_table_iter_init (&iter, a_gendata->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
val2 = g_hash_table_lookup (b_gendata->hash, key);
compared_any = TRUE;
if ( !val2
|| !g_variant_equal (val, val2)) {
diff_found = TRUE;
_setting_diff_add_result (*results, key, a_result);
}
}
g_hash_table_iter_init (&iter, b_gendata->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
val2 = g_hash_table_lookup (a_gendata->hash, key);
compared_any = TRUE;
if ( !val2
|| !g_variant_equal (val, val2)) {
diff_found = TRUE;
_setting_diff_add_result (*results, key, b_result);
}
}
}
} else {
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;
if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
continue;
compared_any = TRUE;
if (b) {
gboolean different;
different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
if (different) {
gboolean a_is_default, b_is_default;
GValue value = G_VALUE_INIT;
g_value_init (&value, prop_spec->value_type);
g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
a_is_default = g_param_value_defaults (prop_spec, &value);
g_value_reset (&value);
g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
b_is_default = g_param_value_defaults (prop_spec, &value);
g_value_unset (&value);
if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) {
if (!a_is_default)
r |= a_result;
if (!b_is_default)
r |= b_result;
} else {
r |= a_result | b_result;
if (a_is_default)
r |= a_result_default;
if (b_is_default)
r |= b_result_default;
}
}
} else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0)
r = a_result; /* only in A */
else {
GValue value = G_VALUE_INIT;
g_value_init (&value, prop_spec->value_type);
g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
if (!g_param_value_defaults (prop_spec, &value))
r |= a_result;
else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)
r |= a_result | a_result_default;
g_value_unset (&value);
}
if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
diff_found = TRUE;
_setting_diff_add_result (*results, prop_spec->name, r);
}
}
}
if (!compared_any && !b) {
/* special case: the setting has no properties, and the opposite
* setting @b is not given. The settings differ, and we signal that
* by returning an empty results hash. */
diff_found = TRUE;
}
if (diff_found) {
/* if there is a difference, we always return FALSE. It also means, we might
* have allocated a new @results hash, and return it to the caller. */
return FALSE;
} else {
if (results_created) {
/* the allocated hash is unused. Clear it again. */
g_hash_table_destroy (*results);
*results = NULL;
} else {
/* we found no diff, and return false. However, the input
* @result is returned unmodified. */
}
return TRUE;
}
}
/**
* nm_setting_enumerate_values:
* @setting: the #NMSetting
* @func: (scope call): user-supplied function called for each property of the setting
* @user_data: user data passed to @func at each invocation
*
* Iterates over each property of the #NMSetting object, calling the supplied
* user function for each property.
**/
void
nm_setting_enumerate_values (NMSetting *setting,
NMSettingValueIterFn func,
gpointer user_data)
{
const NMSettInfoSetting *sett_info;
guint i;
g_return_if_fail (NM_IS_SETTING (setting));
g_return_if_fail (func != NULL);
sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (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
* @func with each value. */
n_properties = _nm_setting_gendata_get_all (setting, &names, NULL);
if (n_properties > 0) {
gs_strfreev char **keys = g_strdupv ((char **) names);
GHashTable *h = _gendata_hash (setting, FALSE)->hash;
for (i = 0; i < n_properties; i++) {
GValue value = G_VALUE_INIT;
GVariant *val = g_hash_table_lookup (h, keys[i]);
if (!val) {
/* was deleted in the meantime? Skip */
continue;
}
g_value_init (&value, G_TYPE_VARIANT);
g_value_set_variant (&value, val);
/* call it will GParamFlags 0. It shall indicate that this
* is not a "real" GObject property. */
func (setting, keys[i], &value, 0, user_data);
g_value_unset (&value);
}
}
return;
}
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);
}
}
/**
* _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;
}
/**
* _nm_setting_clear_secrets:
* @setting: the #NMSetting
*
* Resets and clears any secrets in the setting. Secrets should be added to the
* setting only when needed, and cleared immediately after use to prevent
* leakage of information.
*
* Returns: %TRUE if the setting changed at all
**/
gboolean
_nm_setting_clear_secrets (NMSetting *setting)
{
const NMSettInfoSetting *sett_info;
gboolean changed = FALSE;
guint i;
g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
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;
g_value_init (&value, prop_spec->value_type);
g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
if (!g_param_value_defaults (prop_spec, &value)) {
g_param_value_set_default (prop_spec, &value);
g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
changed = TRUE;
}
g_value_unset (&value);
}
}
return changed;
}
static gboolean
clear_secrets_with_flags (NMSetting *setting,
GParamSpec *pspec,
NMSettingClearSecretsWithFlagsFn func,
gpointer user_data)
{
NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
gboolean changed = FALSE;
g_return_val_if_fail (!NM_IS_SETTING_VPN (setting), FALSE);
/* Clear the secret if the user function says to do so */
if (!nm_setting_get_secret_flags (setting, pspec->name, &flags, NULL))
g_return_val_if_reached (FALSE);
if (func (setting, pspec->name, flags, user_data) == TRUE) {
GValue value = G_VALUE_INIT;
g_value_init (&value, pspec->value_type);
g_object_get_property (G_OBJECT (setting), pspec->name, &value);
if (!g_param_value_defaults (pspec, &value)) {
g_param_value_set_default (pspec, &value);
g_object_set_property (G_OBJECT (setting), pspec->name, &value);
changed = TRUE;
}
g_value_unset (&value);
}
return changed;
}
/**
* _nm_setting_clear_secrets_with_flags:
* @setting: the #NMSetting
* @func: (scope call): function to be called to determine whether a
* specific secret should be cleared or not
* @user_data: caller-supplied data passed to @func
*
* Clears and frees secrets determined by @func.
*
* Returns: %TRUE if the setting changed at all
**/
gboolean
_nm_setting_clear_secrets_with_flags (NMSetting *setting,
NMSettingClearSecretsWithFlagsFn func,
gpointer user_data)
{
const NMSettInfoSetting *sett_info;
gboolean changed = FALSE;
guint i;
g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
g_return_val_if_fail (func != NULL, FALSE);
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;
}
/**
* _nm_setting_need_secrets:
* @setting: the #NMSetting
*
* 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
* guide to what secrets may be required, because in some circumstances, there
* is no way to conclusively determine exactly which secrets are needed.
*
* Returns: (transfer container) (element-type utf8): a #GPtrArray containing
* the property names of secrets of the #NMSetting which may be required; the
* caller owns the array and must free it with g_ptr_array_free(), but must not
* free the elements.
**/
GPtrArray *
_nm_setting_need_secrets (NMSetting *setting)
{
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);
return secrets;
}
static int
update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError **error)
{
const NMSettInfoProperty *property;
GParamSpec *prop_spec;
GValue prop_value = { 0, };
property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), key);
if (!property) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_PROPERTY_NOT_FOUND,
_("secret not found"));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
return NM_SETTING_UPDATE_SECRET_ERROR;
}
/* Silently ignore non-secrets */
prop_spec = property->param_spec;
if (!prop_spec || !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
if ( g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)
&& G_IS_PARAM_SPEC_STRING (prop_spec)) {
/* String is expected to be a common case. Handle it specially and check
* whether the value is already set. Otherwise, we just reset the
* property and assume the value got modified.
*/
char *v;
g_object_get (G_OBJECT (setting), prop_spec->name, &v, NULL);
if (g_strcmp0 (v, g_variant_get_string (value, NULL)) == 0) {
g_free (v);
return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
}
g_free (v);
}
g_value_init (&prop_value, prop_spec->value_type);
set_property_from_dbus (property, value, &prop_value);
g_object_set_property (G_OBJECT (setting), prop_spec->name, &prop_value);
g_value_unset (&prop_value);
return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
}
/**
* _nm_setting_update_secrets:
* @setting: the #NMSetting
* @secrets: a #GVariant of type #NM_VARIANT_TYPE_SETTING, mapping property
* names to secrets.
* @error: location to store error, or %NULL
*
* Update the setting's secrets, given a dictionary of secrets intended for that
* setting (deserialized from D-Bus for example).
*
* Returns: an #NMSettingUpdateSecretResult
**/
NMSettingUpdateSecretResult
_nm_setting_update_secrets (NMSetting *setting, GVariant *secrets, GError **error)
{
GVariantIter iter;
const char *secret_key;
GVariant *secret_value;
GError *tmp_error = NULL;
NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_UPDATE_SECRET_ERROR);
g_return_val_if_fail (g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING), NM_SETTING_UPDATE_SECRET_ERROR);
if (error)
g_return_val_if_fail (*error == NULL, NM_SETTING_UPDATE_SECRET_ERROR);
g_variant_iter_init (&iter, secrets);
while (g_variant_iter_next (&iter, "{&sv}", &secret_key, &secret_value)) {
int success;
success = NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
g_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
g_variant_unref (secret_value);
if (success == NM_SETTING_UPDATE_SECRET_ERROR) {
g_propagate_error (error, tmp_error);
return NM_SETTING_UPDATE_SECRET_ERROR;
}
if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
}
return result;
}
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;
nm_assert (NM_IS_SETTING (setting));
nm_assert (secret_name);
property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), secret_name);
return property
&& property->param_spec
&& NM_FLAGS_HAS (property->param_spec->flags, NM_SETTING_PARAM_SECRET);
}
gboolean
_nm_setting_property_is_regular_secret_flags (NMSetting *setting,
const char *secret_flags_name)
{
const NMSettInfoProperty *property;
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,
NMSettingSecretFlags *out_flags,
GError **error)
{
gs_free char *secret_flags_name_free = NULL;
const char *secret_flags_name;
NMSettingSecretFlags flags;
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),
secret_flags_name,
&flags,
NULL);
NM_SET_OUT (out_flags, flags);
return TRUE;
}
/**
* nm_setting_get_secret_flags:
* @setting: the #NMSetting
* @secret_name: the secret key name to get flags for
* @out_flags: on success, the #NMSettingSecretFlags for the secret
* @error: location to store error, or %NULL
*
* For a given secret, retrieves the #NMSettingSecretFlags describing how to
* handle that secret.
*
* Returns: %TRUE on success (if the given secret name was a valid property of
* this setting, and if that property is secret), %FALSE if not
**/
gboolean
nm_setting_get_secret_flags (NMSetting *setting,
const char *secret_name,
NMSettingSecretFlags *out_flags,
GError **error)
{
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, out_flags, error);
}
static gboolean
set_secret_flags (NMSetting *setting,
const char *secret_name,
NMSettingSecretFlags flags,
GError **error)
{
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;
}
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));
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;
}
/**
* nm_setting_set_secret_flags:
* @setting: the #NMSetting
* @secret_name: the secret key name to set flags for
* @flags: the #NMSettingSecretFlags for the secret
* @error: location to store error, or %NULL
*
* For a given secret, stores the #NMSettingSecretFlags describing how to
* handle that secret.
*
* Returns: %TRUE on success (if the given secret name was a valid property of
* this setting, and if that property is secret), %FALSE if not
**/
gboolean
nm_setting_set_secret_flags (NMSetting *setting,
const char *secret_name,
NMSettingSecretFlags flags,
GError **error)
{
g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
g_return_val_if_fail (secret_name != NULL, 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, flags, error);
}
/**
* nm_setting_to_string:
* @setting: the #NMSetting
*
* Convert the setting (including secrets!) into a string. For debugging
* purposes ONLY, should NOT be used for serialization of the setting,
* or machine-parsed in any way. The output format is not guaranteed to
* be stable and may change at any time.
*
* Returns: an allocated string containing a textual representation of the
* setting's properties and values, which the caller should
* free with g_free()
**/
char *
nm_setting_to_string (NMSetting *setting)
{
GString *string;
gs_unref_variant GVariant *variant = NULL;
GVariant *child;
GVariantIter iter;
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);
g_variant_iter_init (&iter, variant);
while ((child = g_variant_iter_next_value (&iter))) {
gs_free char *name = NULL;
gs_free char *value_str = NULL;
gs_unref_variant GVariant *value = NULL;
g_variant_get (child, "{sv}", &name, &value);
value_str = g_variant_print (value, FALSE);
g_string_append_printf (string, "\t%s : %s\n", name, value_str);
}
return g_string_free (string, FALSE);
}
GVariant *
_nm_setting_get_deprecated_virtual_interface_name (const NMSettInfoSetting *sett_info,
guint property_idx,
NMConnection *connection,
NMSetting *setting,
NMConnectionSerializationFlags flags)
{
NMSettingConnection *s_con;
if (!connection)
return NULL;
s_con = nm_connection_get_setting_connection (connection);
if (!s_con)
return NULL;
if (nm_setting_connection_get_interface_name (s_con))
return g_variant_new_string (nm_setting_connection_get_interface_name (s_con));
else
return NULL;
}
/*****************************************************************************/
static GenData *
_gendata_hash (NMSetting *setting, gboolean create_if_necessary)
{
NMSettingPrivate *priv;
nm_assert (NM_IS_SETTING (setting));
priv = NM_SETTING_GET_PRIVATE (setting);
if (G_UNLIKELY (!priv->gendata)) {
if (!create_if_necessary)
return NULL;
priv->gendata = g_slice_new (GenData);
priv->gendata->hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
priv->gendata->names = NULL;
priv->gendata->values = NULL;
}
return priv->gendata;
}
GHashTable *
_nm_setting_gendata_hash (NMSetting *setting, gboolean create_if_necessary)
{
GenData *gendata;
gendata = _gendata_hash (setting, create_if_necessary);
return gendata ? gendata->hash : NULL;
}
void
_nm_setting_gendata_notify (NMSetting *setting,
gboolean names_changed)
{
GenData *gendata;
gendata = _gendata_hash (setting, FALSE);
if (!gendata)
return;
nm_clear_g_free (&gendata->values);
if (names_changed) {
/* if only the values changed, it's sufficient to invalidate the
* values cache. Otherwise, the names cache must be invalidated too. */
nm_clear_g_free (&gendata->names);
}
/* Note, that currently there is now way to notify the subclass when gendata changed.
* gendata is only changed in two situations:
* 1) from within NMSetting itself, for example when creating a NMSetting instance
* from keyfile or a D-Bus GVariant.
* 2) actively from the subclass itself
* For 2), we don't need the notification, because the subclass knows that something
* changed.
* For 1), we currently don't need the notification either, because all that the subclass
* currently would do, is emit a g_object_notify() signal. However, 1) only happens when
* the setting instance is newly created, at that point, nobody listens to the signal.
*
* If we ever need it, then we would need to call a virtual function to notify the subclass
* that gendata changed. */
}
GVariant *
nm_setting_gendata_get (NMSetting *setting,
const char *name)
{
GenData *gendata;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
g_return_val_if_fail (name, NULL);
gendata = _gendata_hash (setting, FALSE);
return gendata ? g_hash_table_lookup (gendata->hash, name) : NULL;
}
guint
_nm_setting_gendata_get_all (NMSetting *setting,
const char *const**out_names,
GVariant *const**out_values)
{
GenData *gendata;
GHashTable *hash;
guint i, len;
nm_assert (NM_IS_SETTING (setting));
gendata = _gendata_hash (setting, FALSE);
if (!gendata)
goto out_zero;
hash = gendata->hash;
len = g_hash_table_size (hash);
if (len == 0)
goto out_zero;
if (!out_names && !out_values)
return len;
if (G_UNLIKELY (!gendata->names)) {
gendata->names = nm_utils_strdict_get_keys (hash,
TRUE,
NULL);
}
if (out_values) {
if (G_UNLIKELY (!gendata->values)) {
gendata->values = g_new (GVariant *, len + 1);
for (i = 0; i < len; i++)
gendata->values[i] = g_hash_table_lookup (hash, gendata->names[i]);
gendata->values[i] = NULL;
}
*out_values = gendata->values;
}
NM_SET_OUT (out_names, (const char *const*) gendata->names);
return len;
out_zero:
NM_SET_OUT (out_names, NULL);
NM_SET_OUT (out_values, NULL);
return 0;
}
/**
* nm_setting_gendata_get_all_names:
* @setting: the #NMSetting
* @out_len: (allow-none): (out):
*
* Gives the number of generic data elements and optionally returns all their
* key names and values. This API is low level access and unless you know what you
* are doing, it might not be what you want.
*
* Returns: (array length=out_len zero-terminated=1) (transfer none):
* A %NULL terminated array of key names. If no names are present, this returns
* %NULL. The returned array and the names are owned by %NMSetting and might be invalidated
* soon.
*
* Since: 1.14
**/
const char *const*
nm_setting_gendata_get_all_names (NMSetting *setting,
guint *out_len)
{
const char *const*names;
guint len;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
len = _nm_setting_gendata_get_all (setting, &names, NULL);
NM_SET_OUT (out_len, len);
return names;
}
/**
* nm_setting_gendata_get_all_values:
* @setting: the #NMSetting
*
* Gives the number of generic data elements and optionally returns all their
* key names and values. This API is low level access and unless you know what you
* are doing, it might not be what you want.
*
* Returns: (array zero-terminated=1) (transfer none):
* A %NULL terminated array of #GVariant. If no data is present, this returns
* %NULL. The returned array and the variants are owned by %NMSetting and might be invalidated
* soon. The sort order of nm_setting_gendata_get_all_names() and nm_setting_gendata_get_all_values()
* is consistent. That means, the nth value has the nth name returned by nm_setting_gendata_get_all_names().
*
* Since: 1.14
**/
GVariant *const*
nm_setting_gendata_get_all_values (NMSetting *setting)
{
GVariant *const*values;
g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
_nm_setting_gendata_get_all (setting, NULL, &values);
return values;
}
void
_nm_setting_gendata_to_gvalue (NMSetting *setting,
GValue *value)
{
GenData *gendata;
GHashTable *new;
const char *key;
GVariant *val;
GHashTableIter iter;
nm_assert (NM_IS_SETTING (setting));
nm_assert (value);
nm_assert (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_HASH_TABLE));
new = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
gendata = _gendata_hash (setting, FALSE);
if (gendata) {
g_hash_table_iter_init (&iter, gendata->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val))
g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
}
g_value_take_boxed (value, new);
}
gboolean
_nm_setting_gendata_reset_from_hash (NMSetting *setting,
GHashTable *new)
{
GenData *gendata;
GHashTableIter iter;
const char *key;
GVariant *val;
guint num;
nm_assert (NM_IS_SETTING (setting));
nm_assert (new);
num = new ? g_hash_table_size (new) : 0;
gendata = _gendata_hash (setting, num > 0);
if (num == 0) {
if ( !gendata
|| g_hash_table_size (gendata->hash) == 0)
return FALSE;
g_hash_table_remove_all (gendata->hash);
_nm_setting_gendata_notify (setting, TRUE);
return TRUE;
}
/* let's not bother to find out whether the new hash has any different
* content the the current gendata. Just replace it. */
g_hash_table_remove_all (gendata->hash);
if (num > 0) {
g_hash_table_iter_init (&iter, new);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val))
g_hash_table_insert (gendata->hash, g_strdup (key), g_variant_ref (val));
}
_nm_setting_gendata_notify (setting, TRUE);
return TRUE;
}
/*****************************************************************************/
static void
nm_setting_init (NMSetting *setting)
{
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMSetting *setting = NM_SETTING (object);
switch (prop_id) {
case PROP_NAME:
g_value_set_string (value, nm_setting_get_name (setting));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
finalize (GObject *object)
{
NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
if (priv->gendata) {
g_free (priv->gendata->names);
g_free (priv->gendata->values);
g_hash_table_unref (priv->gendata->hash);
g_slice_free (GenData, priv->gendata);
}
G_OBJECT_CLASS (nm_setting_parent_class)->finalize (object);
}
static void
nm_setting_class_init (NMSettingClass *setting_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
GModule *self_module;
gpointer func;
/* loading libnm and legacy libraries libnm-util/libnm-glib at the same
* time is not supported. The reason is, that both libraries use the same
* glib type names ("NMSetting"), and glib does not support namespacing
* to allow for that.
*
* Arbitrarily, add a check here, see whether a known symbol from libnm-util
* is present. If it is, it indicates that the process is borked and we
* abort. */
self_module = g_module_open (NULL, 0);
if (g_module_symbol (self_module, "nm_util_get_private", &func))
g_error ("libnm-util symbols detected; Mixing libnm with libnm-util/libnm-glib is not supported");
g_module_close (self_module);
g_type_class_add_private (setting_class, sizeof (NMSettingPrivate));
object_class->get_property = get_property;
object_class->finalize = finalize;
setting_class->update_one_secret = update_one_secret;
setting_class->get_secret_flags = get_secret_flags;
setting_class->set_secret_flags = set_secret_flags;
setting_class->compare_property = compare_property;
setting_class->clear_secrets_with_flags = clear_secrets_with_flags;
/**
* NMSetting:name:
*
* The setting's name, which uniquely identifies the setting within the
* connection. Each setting type has a name unique to that type, for
* example "ppp" or "802-11-wireless" or "802-3-ethernet".
**/
g_object_class_install_property
(object_class, PROP_NAME,
g_param_spec_string (NM_SETTING_NAME, "", "",
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
}