NetworkManager/src/libnm-core-impl/nm-setting.c
Thomas Haller 2953ebccba
glib-aux: rename nm_utils_array_find_binary_search() to nm_array_bsearch()
The "nm_utils_" prefix is just too verbose. Drop it.
Also, Posix has a bsearch function. As this function
is similar, rename it.

Note that currently the arguments are provided in differnt
order from bsearch(). That will be partly addressed next.
That is the main reason for the rename. The next commit
will swap the arguments, so do a rename first to get a compilation
error when backporting a patch that uses the changed API.
2022-09-28 13:30:43 +02:00

4154 lines
152 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2007 - 2011 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
#include "libnm-core-impl/nm-default-libnm-core.h"
#include "nm-setting.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-glib-aux/nm-ref-string.h"
#include "libnm-glib-aux/nm-secret-utils.h"
#include "nm-property-compare.h"
#include "nm-setting-private.h"
#include "nm-utils-private.h"
#include "nm-utils.h"
/**
* 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;
NM_GOBJECT_PROPERTIES_DEFINE(NMSetting, PROP_NAME, );
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 gboolean set_property_from_dbus(const NMSettInfoProperty *property_info,
GVariant *src_value,
GValue *dst_value);
/*****************************************************************************/
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_get_base_type_priority(NMSetting *setting)
{
g_return_val_if_fail(NM_IS_SETTING(setting), NM_SETTING_PRIORITY_INVALID);
return nm_meta_setting_info_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 (NM_IN_STRSET(slave_type, NM_SETTING_BOND_SETTING_NAME, NM_SETTING_VRF_SETTING_NAME)) {
/* pass */
} else if (nm_streq(slave_type, NM_SETTING_BRIDGE_SETTING_NAME))
port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME;
else if (nm_streq(slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
port_type = NM_SETTING_OVS_PORT_SETTING_NAME;
else if (nm_streq(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
else if (nm_streq(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;
}
gboolean
_nm_properties_override_assert(const NMSettInfoProperty *prop_info)
{
#if NM_MORE_ASSERTS
nm_assert(prop_info);
nm_assert((!!prop_info->name) != (!!prop_info->param_spec));
nm_assert(!prop_info->param_spec || !prop_info->name
|| nm_streq0(prop_info->name, prop_info->param_spec->name));
if (prop_info->property_type) {
const NMSettInfoPropertType *property_type = prop_info->property_type;
/* we always require a dbus_type. */
nm_assert(property_type->dbus_type);
if (property_type->typdata_from_dbus.gprop_fcn)
nm_assert(property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop);
if (property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop)
nm_assert(prop_info->param_spec);
if (!prop_info->param_spec) {
/* if we don't have a param_spec, we cannot have typdata_from_dbus.gprop_fcn. */
nm_assert(property_type->from_dbus_fcn || !property_type->typdata_from_dbus.gprop_fcn);
}
}
#endif
return TRUE;
}
static NMSettInfoSetting _sett_info_settings[_NM_META_SETTING_TYPE_NUM];
const NMSettInfoSetting *
nmtst_sett_info_settings(void)
{
return _sett_info_settings;
}
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,
guint16 property_infos_len,
NMSettingClass *setting_class)
{
const NMSettInfoProperty **arr;
guint16 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;
}
static int
_property_lookup_by_param_spec_sort(gconstpointer p_a, gconstpointer p_b, gpointer user_data)
{
const NMSettInfoPropertLookupByParamSpec *a = p_a;
const NMSettInfoPropertLookupByParamSpec *b = p_b;
NM_CMP_DIRECT(a->param_spec_as_uint, b->param_spec_as_uint);
return 0;
}
void
_nm_setting_class_commit(NMSettingClass *setting_class,
NMMetaSettingType meta_type,
const NMSettInfoSettDetail *detail,
GArray *properties_override,
gint16 private_offset)
{
NMSettInfoSetting *sett_info;
gs_free GParamSpec **property_specs = NULL;
guint n_property_specs;
NMSettInfoPropertLookupByParamSpec *lookup_by_iter;
guint override_len;
guint i;
guint16 j;
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);
property_specs =
g_object_class_list_properties(G_OBJECT_CLASS(setting_class), &n_property_specs);
if (!properties_override) {
override_len = 0;
properties_override = _nm_sett_info_property_override_create_array_sized(n_property_specs);
} else {
override_len = properties_override->len;
for (i = 0; i < override_len; i++) {
NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i);
nm_assert((!!p->name) != (!!p->param_spec));
if (!p->name) {
nm_assert(p->param_spec);
p->name = p->param_spec->name;
} else
nm_assert(!p->param_spec);
}
}
#if NM_MORE_ASSERTS > 10
/* assert that properties_override is constructed consistently. */
for (i = 0; i < override_len; i++) {
const NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i);
gboolean found = FALSE;
guint k;
nm_assert(
!_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data,
i,
p->name));
for (k = 0; k < n_property_specs; k++) {
if (!nm_streq(property_specs[k]->name, p->name))
continue;
nm_assert(!found);
found = TRUE;
nm_assert(p->param_spec == property_specs[k]);
}
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;
p = nm_g_array_append_new(properties_override, NMSettInfoProperty);
memset(p, 0, sizeof(*p));
p->name = name;
p->param_spec = property_specs[i];
}
for (i = 0; i < properties_override->len; i++) {
NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i);
GType vtype;
if (p->property_type)
goto has_property_type;
nm_assert(p->param_spec);
vtype = p->param_spec->value_type;
if (vtype == G_TYPE_STRING) {
/* The "name" property is a bit special because it's defined in the
* parent class NMSetting. We set the property_type here, because
* it's more convenient (albeit a bit ugly).
*
* FIXME: let _nm_sett_info_property_override_create_array() always add
* the handling of the name property.*/
nm_assert(nm_streq(p->name, NM_SETTING_NAME));
nm_assert(!NM_FLAGS_HAS(p->param_spec->flags, G_PARAM_WRITABLE));
p->property_type = &nm_sett_info_propert_type_setting_name;
goto has_property_type;
}
if (vtype == G_TYPE_STRV)
p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
G_VARIANT_TYPE_STRING_ARRAY,
.compare_fcn = _nm_setting_property_compare_fcn_default,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
.from_dbus_is_full = TRUE);
else
nm_assert_not_reached();
has_property_type:
nm_assert(p->property_type);
nm_assert(p->property_type->dbus_type);
nm_assert(g_variant_type_string_is_valid((const char *) p->property_type->dbus_type));
}
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMSettInfoProperty, name) == 0);
g_array_sort(properties_override, nm_strcmp_p);
setting_class->setting_info = &nm_meta_setting_infos[meta_type];
sett_info->setting_class = setting_class;
if (private_offset == NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS) {
int o;
o = g_type_class_get_instance_private_offset(setting_class);
nm_assert(o != NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
nm_assert(o > G_MININT16);
nm_assert(o < 0);
private_offset = o;
}
sett_info->private_offset = private_offset;
if (detail)
sett_info->detail = *detail;
nm_assert(properties_override->len > 0);
nm_assert(properties_override->len < G_MAXUINT16);
sett_info->property_infos_len = properties_override->len;
sett_info->property_infos =
nm_memdup(properties_override->data, sizeof(NMSettInfoProperty) * properties_override->len);
sett_info->property_infos_sorted = _property_infos_sort(sett_info->property_infos,
sett_info->property_infos_len,
setting_class);
nm_assert(sett_info->property_infos_len < G_MAXUINT16);
sett_info->property_lookup_by_param_spec_len = 0;
for (j = 0; j < sett_info->property_infos_len; j++) {
if (sett_info->property_infos[j].param_spec) {
sett_info->property_lookup_by_param_spec_len++;
}
}
sett_info->property_lookup_by_param_spec =
g_new(NMSettInfoPropertLookupByParamSpec, sett_info->property_lookup_by_param_spec_len);
lookup_by_iter =
(NMSettInfoPropertLookupByParamSpec *) sett_info->property_lookup_by_param_spec;
for (j = 0; j < sett_info->property_infos_len; j++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[j];
if (property_info->param_spec) {
*(lookup_by_iter++) = (NMSettInfoPropertLookupByParamSpec){
.param_spec_as_uint = (uintptr_t) ((gpointer) property_info->param_spec),
.property_info = property_info,
};
}
}
g_qsort_with_data(sett_info->property_lookup_by_param_spec,
sett_info->property_lookup_by_param_spec_len,
sizeof(NMSettInfoPropertLookupByParamSpec),
_property_lookup_by_param_spec_sort,
NULL);
g_array_free(properties_override, TRUE);
}
const NMSettInfoProperty *
_nm_sett_info_setting_get_property_info(const NMSettInfoSetting *sett_info,
const char *property_name)
{
const NMSettInfoProperty *property_info;
gssize idx;
nm_assert(property_name);
if (!sett_info)
return NULL;
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMSettInfoProperty, name) == 0);
idx = nm_array_find_bsearch(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_info = &sett_info->property_infos[idx];
nm_assert(idx == 0 || strcmp(property_info[-1].name, property_info[0].name) < 0);
nm_assert(idx == sett_info->property_infos_len - 1
|| strcmp(property_info[0].name, property_info[1].name) < 0);
return property_info;
}
const NMSettInfoSetting *
_nm_setting_class_get_sett_info(NMSettingClass *setting_class)
{
const NMSettInfoSetting *sett_info;
if (!NM_IS_SETTING_CLASS(setting_class) || !setting_class->setting_info)
return NULL;
nm_assert(setting_class->setting_info->meta_type < G_N_ELEMENTS(_sett_info_settings));
sett_info = &_sett_info_settings[setting_class->setting_info->meta_type];
nm_assert(sett_info->setting_class == setting_class);
return sett_info;
}
const NMSettInfoProperty *
_nm_sett_info_property_lookup_by_param_spec(const NMSettInfoSetting *sett_info,
const GParamSpec *param_spec)
{
NMSettInfoPropertLookupByParamSpec needle;
int imin;
int imax;
int imid;
int cmp;
nm_assert(sett_info);
nm_assert(param_spec);
/* ensure that "int" is large enough to contain the index variables. */
G_STATIC_ASSERT_EXPR(sizeof(int) > sizeof(sett_info->property_lookup_by_param_spec_len));
if (sett_info->property_lookup_by_param_spec_len == 0)
return NULL;
needle.param_spec_as_uint = (uintptr_t) ((gpointer) param_spec);
imin = 0;
imax = sett_info->property_lookup_by_param_spec_len - 1;
while (imin <= imax) {
imid = imin + (imax - imin) / 2;
cmp = _property_lookup_by_param_spec_sort(&sett_info->property_lookup_by_param_spec[imid],
&needle,
NULL);
if (cmp == 0)
return sett_info->property_lookup_by_param_spec[imid].property_info;
if (cmp < 0)
imin = imid + 1;
else
imax = imid - 1;
}
return NULL;
}
/*****************************************************************************/
void
_nm_setting_emit_property_changed(NMSetting *setting)
{
/* Some settings have "properties" that are not implemented as GObject properties.
*
* For example:
*
* - gendata-base settings like NMSettingEthtool. Here properties are just
* GVariant values in the gendata hash.
*
* - NMSettingWireGuard's peers are not backed by a GObject property. Instead
* there is C-API to access/modify peers.
*
* We still want to emit property-changed notifications for such properties,
* in particular because NMConnection registers to such signals to re-emit
* it as NM_CONNECTION_CHANGED signal. In fact, there are unlikely any other
* uses of such a property-changed signal, because generally it doesn't make
* too much sense.
*
* So, instead of adding yet another (artificial) signal "setting-changed",
* hijack the "notify" signal and just notify about changes of the "name".
* Of course, the "name" doesn't really ever change, because it's tied to
* the GObject's type.
*/
_notify(setting, PROP_NAME);
}
/*****************************************************************************/
gboolean
_nm_setting_use_legacy_property(NMSetting *setting,
GVariant *connection_dict,
const char *legacy_property,
const char *new_property)
{
gs_unref_variant GVariant *setting_dict = NULL;
gs_unref_variant GVariant *value = NULL;
if (!connection_dict) {
/* we also allow the caller to provide no connection_dict.
*
* We hit this code bug when being called by update_one_secret().
* In this case, we use the legacy property, because we are not
* sophisticated enough to mediate between deprecated and legacy
* properties...
*
* However, in practice this code is unreachable, because update_one_secret()
* only ends up calling from_dbus_fcn() for certain properties, and none
* of those are actually deprecated (for now). So this cannot really happen. */
return nm_assert_unreachable_val(FALSE);
}
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)
return TRUE;
nm_clear_pointer(&value, g_variant_unref);
/* Otherwise, clients always prefer new properties sent from the daemon. */
if (!_nm_utils_is_manager_process)
return FALSE;
/* The daemon prefers the legacy property if it exists. */
value = g_variant_lookup_value(setting_dict, legacy_property, NULL);
return !!value;
}
/*****************************************************************************/
static gboolean
_property_direct_set_string(const NMSettInfoSetting *sett_info,
const NMSettInfoProperty *property_info,
NMSetting *setting,
const char *src)
{
char **dst;
char *s;
nm_assert(property_info->property_type->direct_type == NM_VALUE_TYPE_STRING);
nm_assert(((!!property_info->direct_set_string_ascii_strdown)
+ (!!property_info->direct_set_string_strip)
+ (!!property_info->direct_string_is_refstr)
+ (property_info->direct_set_string_mac_address_len > 0)
+ (property_info->direct_set_string_ip_address_addr_family != 0))
<= (property_info->direct_hook.set_string_fcn ? 0 : 1));
if (property_info->direct_hook.set_string_fcn) {
return property_info->direct_hook.set_string_fcn(sett_info, property_info, setting, src);
}
dst = _nm_setting_get_private_field(setting, sett_info, property_info);
if (property_info->direct_string_is_refstr) {
nm_assert(property_info->param_spec);
nm_assert(!NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET));
return nm_ref_string_reset_str_upcast((const char **) dst, src);
}
if (property_info->direct_set_string_ascii_strdown) {
s = src ? g_ascii_strdown(src, -1) : NULL;
goto out_take;
}
if (property_info->direct_set_string_strip) {
s = nm_strstrip_dup(src);
goto out_take;
}
if (property_info->direct_set_string_mac_address_len > 0) {
s = _nm_utils_hwaddr_canonical_or_invalid(src,
property_info->direct_set_string_mac_address_len);
goto out_take;
}
if (property_info->direct_set_string_ip_address_addr_family != 0) {
s = _nm_utils_ipaddr_canonical_or_invalid(
property_info->direct_set_string_ip_address_addr_family - 1,
src,
property_info->direct_set_string_ip_address_addr_family_map_zero_to_null);
goto out_take;
} else
nm_assert(!property_info->direct_set_string_ip_address_addr_family_map_zero_to_null);
if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET))
return nm_strdup_reset_secret(dst, src);
return nm_strdup_reset(dst, src);
out_take:
nm_assert(!NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET));
return nm_strdup_reset_take(dst, s);
}
void
_nm_setting_property_get_property_direct(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
NMSetting *setting = NM_SETTING(object);
const NMSettInfoSetting *sett_info;
const NMSettInfoProperty *property_info;
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
nm_assert(sett_info);
property_info = _nm_sett_info_property_lookup_by_param_spec(sett_info, pspec);
if (!property_info)
goto out_fail;
nm_assert(property_info->param_spec == pspec);
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_BOOL:
{
const bool *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_boolean(value, *p_val);
return;
}
case NM_VALUE_TYPE_INT32:
{
const gint32 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_int(value, *p_val);
return;
}
case NM_VALUE_TYPE_UINT32:
{
const guint32 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_uint(value, *p_val);
return;
}
case NM_VALUE_TYPE_INT64:
{
const gint64 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_int64(value, *p_val);
return;
}
case NM_VALUE_TYPE_UINT64:
{
const guint64 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_uint64(value, *p_val);
return;
}
case NM_VALUE_TYPE_ENUM:
{
const int *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_enum(value, *p_val);
return;
}
case NM_VALUE_TYPE_FLAGS:
{
const guint *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_flags(value, *p_val);
return;
}
case NM_VALUE_TYPE_STRING:
{
const char *const *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_string(value, *p_val);
return;
}
case NM_VALUE_TYPE_BYTES:
{
const GBytes *const *p_val =
_nm_setting_get_private_field(setting, sett_info, property_info);
g_value_set_boxed(value, *p_val);
return;
}
case NM_VALUE_TYPE_STRV:
{
const NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(p_val->arr, NULL));
return;
}
default:
goto out_fail;
}
return;
out_fail:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
void
_nm_setting_property_set_property_direct(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
NMSetting *setting = NM_SETTING(object);
const NMSettInfoSetting *sett_info;
const NMSettInfoProperty *property_info;
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
nm_assert(sett_info);
property_info = _nm_sett_info_property_lookup_by_param_spec(sett_info, pspec);
if (!property_info)
goto out_fail;
nm_assert(property_info->param_spec == pspec);
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_BOOL:
{
bool *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
gboolean v;
v = g_value_get_boolean(value);
if (*p_val == v)
return;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_INT32:
{
gint32 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
int v;
v = g_value_get_int(value);
if (*p_val == v)
return;
*p_val = v;
/* truncation cannot happen, because the param_spec is supposed to have suitable
* minimum/maximum values so that we are in range for int32. */
nm_assert(*p_val == v);
goto out_notify;
}
case NM_VALUE_TYPE_UINT32:
{
guint32 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
guint v;
v = g_value_get_uint(value);
if (*p_val == v)
return;
*p_val = v;
/* truncation cannot happen, because the param_spec is supposed to have suitable
* minimum/maximum values so that we are in range for uint32. */
nm_assert(*p_val == v);
goto out_notify;
}
case NM_VALUE_TYPE_INT64:
{
gint64 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
gint64 v;
v = g_value_get_int64(value);
if (*p_val == v)
return;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_UINT64:
{
guint64 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
guint64 v;
v = g_value_get_uint64(value);
if (*p_val == v)
return;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_ENUM:
{
int *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
int v;
v = g_value_get_enum(value);
if (*p_val == v)
return;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_FLAGS:
{
guint *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
guint v;
v = g_value_get_flags(value);
if (*p_val == v)
return;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_STRING:
if (!_property_direct_set_string(sett_info,
property_info,
setting,
g_value_get_string(value)))
return;
goto out_notify;
case NM_VALUE_TYPE_BYTES:
{
GBytes **p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
GBytes *v;
_nm_unused gs_unref_bytes GBytes *old = NULL;
v = g_value_get_boxed(value);
if (nm_g_bytes_equal0(*p_val, v))
return;
old = *p_val;
*p_val = v ? g_bytes_ref(v) : NULL;
goto out_notify;
}
case NM_VALUE_TYPE_STRV:
{
NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
const char *const *v;
v = g_value_get_boxed(value);
if (nm_strvarray_equal_strv(p_val->arr, v, -1))
return;
nm_strvarray_set_strv(&p_val->arr, v);
goto out_notify;
}
default:
goto out_fail;
}
return;
out_notify:
/* If explicit-notify would be set, we would need to emit g_object_notify_by_pspec().
*
* Currently we never set that, also because we still support glib 2.40. */
nm_assert(!NM_FLAGS_HAS(pspec->flags, 1 << 30 /* G_PARAM_EXPLICIT_NOTIFY */));
return;
out_fail:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
/*****************************************************************************/
static void
_init_direct(NMSetting *setting)
{
const NMSettInfoSetting *sett_info;
guint16 i;
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
nm_assert(sett_info);
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
/* We don't emit any g_object_notify_by_pspec(), because this is
* only supposed to be called during initialization of the GObject
* instance. */
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_NONE:
break;
case NM_VALUE_TYPE_BOOL:
{
bool *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
gboolean def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(property_info->param_spec);
nm_assert(*p_val == FALSE);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_INT32:
{
gint32 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
int def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_INT(property_info->param_spec);
nm_assert(*p_val == 0);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_UINT32:
{
guint32 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
guint def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_UINT(property_info->param_spec);
nm_assert(*p_val == 0);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_INT64:
{
gint64 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
gint64 def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_INT64(property_info->param_spec);
nm_assert(*p_val == 0);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_UINT64:
{
guint64 *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
guint64 def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(property_info->param_spec);
nm_assert(*p_val == 0);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_ENUM:
{
int *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
int def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_ENUM(property_info->param_spec);
nm_assert(*p_val == 0);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_FLAGS:
{
guint *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
guint def_val;
def_val = NM_G_PARAM_SPEC_GET_DEFAULT_FLAGS(property_info->param_spec);
nm_assert(*p_val == 0);
*p_val = def_val;
break;
}
case NM_VALUE_TYPE_STRING:
nm_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(property_info->param_spec));
nm_assert(!(*((const char *const *)
_nm_setting_get_private_field(setting, sett_info, property_info))));
break;
case NM_VALUE_TYPE_BYTES:
nm_assert(!(*((const GBytes *const *)
_nm_setting_get_private_field(setting, sett_info, property_info))));
break;
case NM_VALUE_TYPE_STRV:
nm_assert(!((const NMValueStrv *)
_nm_setting_get_private_field(setting, sett_info, property_info))
->arr);
break;
default:
nm_assert_not_reached();
break;
}
}
}
static void
_finalize_direct(NMSetting *setting)
{
const NMSettInfoSetting *sett_info;
guint16 i;
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
nm_assert(sett_info);
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
/* We only:
*
* - reset fields where there is something to free. E.g. boolean
* properties are not reset to their default.
* - clear/free properties, without emitting g_object_notify_by_pspec(),
* because this is called only during finalization. */
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_NONE:
case NM_VALUE_TYPE_BOOL:
case NM_VALUE_TYPE_INT32:
case NM_VALUE_TYPE_UINT32:
case NM_VALUE_TYPE_INT64:
case NM_VALUE_TYPE_UINT64:
case NM_VALUE_TYPE_ENUM:
case NM_VALUE_TYPE_FLAGS:
break;
case NM_VALUE_TYPE_STRING:
{
char **p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (property_info->direct_string_is_refstr)
nm_clear_pointer(p_val, nm_ref_string_unref_upcast);
else if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET))
nm_clear_pointer(p_val, nm_free_secret);
else
nm_clear_g_free(p_val);
break;
}
case NM_VALUE_TYPE_BYTES:
{
GBytes **p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
nm_clear_pointer(p_val, g_bytes_unref);
break;
}
case NM_VALUE_TYPE_STRV:
{
NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
nm_clear_pointer(&p_val->arr, g_array_unref);
break;
}
default:
nm_assert_not_reached();
break;
}
}
}
/*****************************************************************************/
GVariant *
_nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
{
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_BOOL:
{
gboolean val;
val = *((bool *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(property_info->param_spec))
return NULL;
return g_variant_ref(nm_g_variant_singleton_b(val));
}
case NM_VALUE_TYPE_INT32:
{
gint32 val;
val = *((gint32 *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_INT(property_info->param_spec))
return NULL;
return nm_g_variant_maybe_singleton_i(val);
}
case NM_VALUE_TYPE_UINT32:
{
guint32 val;
val = *((guint32 *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_UINT(property_info->param_spec))
return NULL;
return g_variant_new_uint32(val);
}
case NM_VALUE_TYPE_INT64:
{
gint64 val;
val = *((gint64 *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_INT64(property_info->param_spec))
return NULL;
return g_variant_new_int64(val);
}
case NM_VALUE_TYPE_UINT64:
{
guint64 val;
val = *((guint64 *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(property_info->param_spec))
return NULL;
return g_variant_new_uint64(val);
}
case NM_VALUE_TYPE_ENUM:
{
int val;
val = *((int *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_ENUM(property_info->param_spec))
return NULL;
return nm_g_variant_maybe_singleton_i(val);
}
case NM_VALUE_TYPE_FLAGS:
{
guint val;
val = *((guint *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!property_info->to_dbus_including_default
&& val == NM_G_PARAM_SPEC_GET_DEFAULT_FLAGS(property_info->param_spec))
return NULL;
return g_variant_new_uint32(val);
}
case NM_VALUE_TYPE_STRING:
{
const char *val;
/* For string properties that are implemented via this function, the default is always NULL.
* In general, having strings default to NULL is most advisable.
*
* Setting "including_default" for a string makes no sense because a
* GVariant of type "s" cannot express NULL. */
nm_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(property_info->param_spec));
nm_assert(!property_info->to_dbus_including_default);
val = *(
(const char *const *) _nm_setting_get_private_field(setting, sett_info, property_info));
if (!val)
return NULL;
if (!val[0])
return g_variant_ref(nm_g_variant_singleton_s_empty());
return g_variant_new_string(val);
}
case NM_VALUE_TYPE_BYTES:
{
const GBytes *val;
/* Bytes have always NULL as default. Setting "including_default" has no defined meaning
* (but it could have). */
nm_assert(!property_info->to_dbus_including_default);
val = *((const GBytes *const *) _nm_setting_get_private_field(setting,
sett_info,
property_info));
if (!val)
return NULL;
return nm_g_bytes_to_variant_ay(val);
}
case NM_VALUE_TYPE_STRV:
{
const NMValueStrv *val;
/* Strv properties have always NULL as default. Setting "including_default" has no defined meaning
* (but it could have). */
nm_assert(!property_info->to_dbus_including_default);
val =
(const NMValueStrv *) _nm_setting_get_private_field(setting, sett_info, property_info);
if (!val->arr)
return NULL;
return g_variant_new_strv((const char *const *) val->arr->data, val->arr->len);
}
default:
return nm_assert_unreachable_val(NULL);
}
}
GVariant *
_nm_setting_property_to_dbus_fcn_direct_mac_address(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
{
const char *val;
nm_assert(property_info->property_type == &nm_sett_info_propert_type_direct_mac_address);
nm_assert(property_info->property_type->direct_type == NM_VALUE_TYPE_STRING);
nm_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(property_info->param_spec));
nm_assert(!property_info->to_dbus_including_default);
val = *((const char *const *) _nm_setting_get_private_field(setting, sett_info, property_info));
return nm_utils_hwaddr_to_dbus(val);
}
GVariant *
_nm_setting_property_to_dbus_fcn_ignore(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
{
return NULL;
}
GVariant *
_nm_setting_property_to_dbus_fcn_gprop(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
{
nm_auto_unset_gvalue GValue prop_value = {
0,
};
GArray *tmp_array;
nm_assert(property_info->param_spec);
nm_assert(property_info->property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_gprop);
g_value_init(&prop_value, property_info->param_spec->value_type);
g_object_get_property(G_OBJECT(setting), property_info->param_spec->name, &prop_value);
if (!property_info->to_dbus_including_default
&& g_param_value_defaults(property_info->param_spec, &prop_value))
return NULL;
switch (property_info->property_type->typdata_to_dbus.gprop_type) {
case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_DEFAULT:
return g_dbus_gvalue_to_gvariant(&prop_value, property_info->property_type->dbus_type);
case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_GARRAY_UINT:
G_STATIC_ASSERT_EXPR(sizeof(guint) == sizeof(guint32));
nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_ARRAY));
tmp_array = g_value_get_boxed(&prop_value);
nm_assert(tmp_array);
return nm_g_variant_new_au((const guint32 *) tmp_array->data, tmp_array->len);
case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT:
nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_HASH_TABLE));
return nm_strdict_to_variant_ass(g_value_get_boxed(&prop_value));
}
return nm_assert_unreachable_val(NULL);
}
gboolean
_nm_setting_property_from_dbus_fcn_ignore(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
{
*out_is_modified = FALSE;
return TRUE;
}
gboolean
_nm_setting_property_from_dbus_fcn_direct_mac_address(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
{
gsize length = 0;
const guint8 *array;
nm_assert(property_info->param_spec);
nm_assert(property_info->property_type == &nm_sett_info_propert_type_direct_mac_address);
nm_assert(g_variant_type_equal(property_info->property_type->dbus_type, "ay"));
nm_assert(
g_variant_type_equal(g_variant_get_type(value), property_info->property_type->dbus_type));
nm_assert(property_info->direct_set_string_mac_address_len > 0);
array = g_variant_get_fixed_array(value, &length, 1);
if (nm_strdup_reset_take(_nm_setting_get_private_field(setting, sett_info, property_info),
length > 0 ? nm_utils_hwaddr_ntoa(array, length) : NULL)) {
g_object_notify_by_pspec(G_OBJECT(setting), property_info->param_spec);
} else
*out_is_modified = FALSE;
return TRUE;
}
gboolean
_nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
{
nm_assert(property_info->param_spec);
nm_assert(NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE));
nm_assert(!NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_CONSTRUCT_ONLY));
nm_assert(!property_info->property_type->typdata_from_dbus.gprop_fcn);
#define _variant_get_value_transform(property_info, value, gtype, gvalue_get, out_val) \
({ \
const NMSettInfoProperty const *_property_info = (property_info); \
const GType _gtype = (gtype); \
GVariant *_value = (value); \
gboolean _success = FALSE; \
\
nm_assert(_property_info->param_spec->value_type == _gtype); \
if (_property_info->property_type->from_dbus_direct_allow_transform) { \
nm_auto_unset_gvalue GValue _gvalue = G_VALUE_INIT; \
\
g_value_init(&_gvalue, _gtype); \
if (_nm_property_variant_to_gvalue(_value, &_gvalue)) { \
*(out_val) = (gvalue_get(&_gvalue)); \
_success = TRUE; \
} \
} \
_success; \
})
*out_is_modified = FALSE;
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_BOOL:
{
bool *p_val;
gboolean v;
if (g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN))
v = g_variant_get_boolean(value);
else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_BOOLEAN,
g_value_get_boolean,
&v))
goto out_error_wrong_dbus_type;
v = !!v;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_INT32:
{
const GParamSpecInt *param_spec;
gint32 *p_val;
int v;
if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) {
G_STATIC_ASSERT(sizeof(int) >= sizeof(gint32));
v = g_variant_get_int32(value);
} else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_INT,
g_value_get_int,
&v))
goto out_error_wrong_dbus_type;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
param_spec = NM_G_PARAM_SPEC_CAST_INT(property_info->param_spec);
if (v < param_spec->minimum || v > param_spec->maximum)
goto out_error_param_spec_validation;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_UINT32:
{
const GParamSpecUInt *param_spec;
guint32 *p_val;
guint v;
if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
G_STATIC_ASSERT(sizeof(guint) >= sizeof(guint32));
v = g_variant_get_uint32(value);
} else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_UINT,
g_value_get_uint,
&v))
goto out_error_wrong_dbus_type;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
param_spec = NM_G_PARAM_SPEC_CAST_UINT(property_info->param_spec);
if (v < param_spec->minimum || v > param_spec->maximum)
goto out_error_param_spec_validation;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_INT64:
{
const GParamSpecInt64 *param_spec;
gint64 *p_val;
gint64 v;
if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT64))
v = g_variant_get_int64(value);
else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_INT64,
g_value_get_int64,
&v))
goto out_error_wrong_dbus_type;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
param_spec = NM_G_PARAM_SPEC_CAST_INT64(property_info->param_spec);
if (v < param_spec->minimum || v > param_spec->maximum)
goto out_error_param_spec_validation;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_UINT64:
{
const GParamSpecUInt64 *param_spec;
guint64 *p_val;
guint64 v;
if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT64))
v = g_variant_get_uint64(value);
else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_UINT64,
g_value_get_uint64,
&v))
goto out_error_wrong_dbus_type;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
param_spec = NM_G_PARAM_SPEC_CAST_UINT64(property_info->param_spec);
if (v < param_spec->minimum || v > param_spec->maximum)
goto out_error_param_spec_validation;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_ENUM:
{
const GParamSpecEnum *param_spec;
int *p_val;
int v;
param_spec = NM_G_PARAM_SPEC_CAST_ENUM(property_info->param_spec);
if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) {
G_STATIC_ASSERT(sizeof(int) >= sizeof(gint32));
v = g_variant_get_int32(value);
} else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_FROM_CLASS(param_spec->enum_class),
g_value_get_flags,
&v))
goto out_error_wrong_dbus_type;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
if (!g_enum_get_value(param_spec->enum_class, v))
goto out_error_param_spec_validation;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_FLAGS:
{
const GParamSpecFlags *param_spec;
guint *p_val;
guint v;
param_spec = NM_G_PARAM_SPEC_CAST_FLAGS(property_info->param_spec);
if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
G_STATIC_ASSERT(sizeof(guint) >= sizeof(guint32));
v = g_variant_get_uint32(value);
} else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_FROM_CLASS(param_spec->flags_class),
g_value_get_flags,
&v))
goto out_error_wrong_dbus_type;
}
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (*p_val == v)
goto out_unchanged;
if ((v & param_spec->flags_class->mask) != v)
goto out_error_param_spec_validation;
*p_val = v;
goto out_notify;
}
case NM_VALUE_TYPE_STRING:
{
gs_free char *v_free = NULL;
const char *v;
gboolean changed;
if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
v = g_variant_get_string(value, NULL);
} else {
if (!_variant_get_value_transform(property_info,
value,
G_TYPE_STRING,
g_value_dup_string,
&v_free))
goto out_error_wrong_dbus_type;
v = v_free;
}
changed = _property_direct_set_string(sett_info, property_info, setting, v);
if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET))
nm_clear_pointer(&v_free, nm_free_secret);
if (!changed)
goto out_unchanged;
goto out_notify;
}
case NM_VALUE_TYPE_BYTES:
{
gs_unref_bytes GBytes *v = NULL;
GBytes **p_val;
nm_assert(!property_info->property_type->from_dbus_direct_allow_transform);
if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING))
goto out_error_wrong_dbus_type;
v = nm_g_bytes_new_from_variant_ay(value);
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (nm_g_bytes_equal0(*p_val, v))
goto out_unchanged;
NM_SWAP(p_val, &v);
goto out_notify;
}
case NM_VALUE_TYPE_STRV:
{
NMValueStrv *p_val;
gs_free const char **ss = NULL;
gsize ss_len;
nm_assert(!property_info->property_type->from_dbus_direct_allow_transform);
if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY))
goto out_error_wrong_dbus_type;
ss = g_variant_get_strv(value, &ss_len);
nm_assert(ss_len <= G_MAXUINT);
p_val = _nm_setting_get_private_field(setting, sett_info, property_info);
if (nm_strvarray_equal_strv(p_val->arr, ss, ss_len))
goto out_unchanged;
nm_strvarray_set_strv(&p_val->arr, ss);
goto out_notify;
}
default:
break;
}
nm_assert_not_reached();
out_unchanged:
return TRUE;
out_notify:
*out_is_modified = TRUE;
g_object_notify_by_pspec(G_OBJECT(setting), property_info->param_spec);
return TRUE;
out_error_wrong_dbus_type:
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
return TRUE;
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_info->property_type->dbus_type
? g_variant_type_peek_string(property_info->property_type->dbus_type)
: (property_info->param_spec
? g_type_name(property_info->param_spec->value_type)
: "(unknown)"),
g_variant_get_type_string(value));
g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), property_info->name);
return FALSE;
out_error_param_spec_validation:
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
return TRUE;
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
_("value of type '%s' is invalid or out of range for property '%s'"),
g_variant_get_type_string(value),
property_info->name);
g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), property_info->name);
return FALSE;
}
gboolean
_nm_setting_property_from_dbus_fcn_gprop(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
{
nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
gs_free_error GError *local = NULL;
nm_assert(property_info->param_spec);
g_value_init(&object_value, property_info->param_spec->value_type);
if (!set_property_from_dbus(property_info, value, &object_value)) {
/* for backward behavior, fail unless best-effort is chosen. */
*out_is_modified = FALSE;
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
return TRUE;
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_info->property_type->dbus_type
? g_variant_type_peek_string(property_info->property_type->dbus_type)
: (property_info->param_spec
? g_type_name(property_info->param_spec->value_type)
: "(unknown)"),
g_variant_get_type_string(value));
g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), property_info->name);
return FALSE;
}
if (!nm_g_object_set_property(G_OBJECT(setting),
property_info->param_spec->name,
&object_value,
&local)) {
*out_is_modified = FALSE;
if (!NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
return TRUE;
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_info->name);
return FALSE;
}
return TRUE;
}
static GVariant *
property_to_dbus(const NMSettInfoSetting *sett_info,
const NMSettInfoProperty *property_info,
NMConnection *connection,
NMSetting *setting,
NMConnectionSerializationFlags flags,
const NMConnectionSerializationOptions *options,
gboolean ignore_flags)
{
GVariant *variant;
nm_assert(property_info->property_type->dbus_type);
if (!property_info->property_type->to_dbus_fcn) {
nm_assert(!property_info->param_spec);
return NULL;
}
nm_assert(!property_info->param_spec
|| NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE)
|| property_info->property_type == &nm_sett_info_propert_type_setting_name);
if (property_info->param_spec && !ignore_flags
&& !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS)) {
if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_LEGACY)
&& !_nm_utils_is_manager_process)
return NULL;
if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE;
if (NM_FLAGS_ANY(flags,
NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED
| NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED
| NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)) {
if (!nm_setting_get_secret_flags(setting,
property_info->param_spec->name,
&f,
NULL))
return NULL;
}
if (!_nm_connection_serialize_secrets(flags, f))
return NULL;
} else {
if (!_nm_connection_serialize_non_secret(flags))
return NULL;
}
}
variant = property_info->property_type
->to_dbus_fcn(sett_info, property_info, connection, setting, flags, options);
nm_g_variant_take_ref(variant);
nm_assert(!variant || g_variant_is_of_type(variant, property_info->property_type->dbus_type));
return variant;
}
static gboolean
set_property_from_dbus(const NMSettInfoProperty *property_info,
GVariant *src_value,
GValue *dst_value)
{
nm_assert(property_info->param_spec);
nm_assert(property_info->property_type->dbus_type);
if (property_info->property_type->typdata_from_dbus.gprop_fcn) {
if (!g_variant_type_equal(g_variant_get_type(src_value),
property_info->property_type->dbus_type))
return FALSE;
property_info->property_type->typdata_from_dbus.gprop_fcn(src_value, dst_value);
return TRUE;
}
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);
return TRUE;
}
return _nm_property_variant_to_gvalue(src_value, dst_value);
}
/**
* _nm_setting_to_dbus:
* @setting: the #NMSetting
* @connection: the #NMConnection containing @setting
* @flags: hash flags, e.g. %NM_CONNECTION_SERIALIZE_ALL
* @options: the #NMConnectionSerializationOptions options to control
* what/how gets serialized.
*
* 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,
const NMConnectionSerializationOptions *options)
{
NMSettingPrivate *priv;
GVariantBuilder builder;
const NMSettInfoSetting *sett_info;
guint n_properties;
guint i;
guint16 j;
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_option_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_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
for (j = 0; j < sett_info->property_infos_len; j++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[j];
gs_unref_variant GVariant *dbus_value = NULL;
dbus_value =
property_to_dbus(sett_info, property_info, connection, setting, flags, options, FALSE);
if (dbus_value) {
g_variant_builder_add(&builder, "{sv}", property_info->name, 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_ptrarray GPtrArray *keys_keep_variant = NULL;
gs_unref_object NMSetting *setting = NULL;
gs_unref_hashtable GHashTable *keys = NULL;
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 = g_object_new(setting_type, NULL);
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
GVariantIter iter;
GVariant *entry, *entry_key;
const char *key;
keys_keep_variant = g_ptr_array_new_with_free_func((GDestroyNotify) g_variant_unref);
keys = g_hash_table_new(nm_str_hash, g_str_equal);
g_variant_iter_init(&iter, setting_dict);
while ((entry = g_variant_iter_next_value(&iter))) {
entry_key = g_variant_get_child_value(entry, 0);
g_ptr_array_add(keys_keep_variant, entry_key);
g_variant_unref(entry);
key = g_variant_get_string(entry_key, NULL);
if (!g_hash_table_add(keys, (char *) 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;
}
}
}
if (!NM_SETTING_GET_CLASS(setting)
->init_from_dbus(setting, keys, setting_dict, connection_dict, parse_flags, error))
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);
}
static gboolean
_property_set_from_dbus(const NMSettInfoSetting *sett_info,
const NMSettInfoProperty *property_info,
NMSetting *setting,
GVariant *connection_dict,
GVariant *value,
NMSettingParseFlags parse_flags,
gboolean *out_is_modified,
GError **error)
{
gs_free_error GError *local = NULL;
NMTernary is_modified = NM_TERNARY_DEFAULT;
gboolean success;
NM_SET_OUT(out_is_modified, FALSE);
if (!property_info->property_type->from_dbus_fcn) {
nm_assert(!property_info->param_spec);
return TRUE;
}
if (property_info->property_type->from_dbus_is_full) {
/* These hooks perform their own type checking, and can coerce/ignore
* a value regardless of the D-Bus type. */
} else if (!g_variant_type_equal(g_variant_get_type(value),
property_info->property_type->dbus_type)) {
/* for backward behavior, fail unless best-effort is chosen. */
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
return TRUE;
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_info->property_type->dbus_type
? g_variant_type_peek_string(property_info->property_type->dbus_type)
: property_info->param_spec ? g_type_name(property_info->param_spec->value_type)
: "(unknown)",
g_variant_get_type_string(value));
g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), property_info->name);
return FALSE;
}
success = property_info->property_type->from_dbus_fcn(sett_info,
property_info,
setting,
connection_dict,
value,
parse_flags,
&is_modified,
&local);
/* We allow the from_dbus_fcn() to leave is_modified at NM_TERNARY_DEFAULT,
* which we assume to also mean that it was modified. That is, we err on the
* side of assuming modification happened. */
NM_SET_OUT(out_is_modified, is_modified != FALSE);
if (!success) {
if (property_info->property_type->from_dbus_is_full) {
/* the error we received from from_dbus_fcn() should be propagated, even
* in non-strict mode. */
} else if (!NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
return TRUE;
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_info->name);
return FALSE;
}
return TRUE;
}
static gboolean
init_from_dbus(NMSetting *setting,
GHashTable *keys,
GVariant *setting_dict,
GVariant *connection_dict,
guint /* NMSettingParseFlags */ parse_flags,
GError **error)
{
const NMSettInfoSetting *sett_info;
guint16 i;
nm_assert(NM_IS_SETTING(setting));
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));
sett_info = _nm_setting_class_get_sett_info(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);
if (keys)
g_hash_table_remove(keys, key);
}
_nm_setting_option_notify(setting, TRUE);
/* Currently, only NMSettingEthtool supports gendata based options, and
* that one has no other properties (except "name"). That means, we
* consumed all options above.
*
* In the future it may be interesting to have settings that are both
* based on gendata and regular properties. In that case, we would need
* to handle this case differently. */
nm_assert(nm_streq(G_OBJECT_TYPE_NAME(setting), "NMSettingEthtool"));
nm_assert(sett_info->property_infos_len == 1);
return TRUE;
}
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
gs_unref_variant GVariant *value = NULL;
gs_free_error GError *local = NULL;
if (property_info->property_type == &nm_sett_info_propert_type_setting_name)
continue;
nm_assert(!property_info->param_spec
|| NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE));
value = g_variant_lookup_value(setting_dict, property_info->name, NULL);
if (!value) {
if (property_info->property_type->missing_from_dbus_fcn
&& !property_info->property_type->missing_from_dbus_fcn(setting,
connection_dict,
property_info->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_info->name);
return FALSE;
}
continue;
}
if (keys)
g_hash_table_remove(keys, property_info->name);
if (!_property_set_from_dbus(sett_info,
property_info,
setting,
connection_dict,
value,
parse_flags,
NULL,
error))
return FALSE;
}
return TRUE;
}
/**
* 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_info;
g_return_val_if_fail(NM_IS_SETTING(setting), NULL);
g_return_val_if_fail(property_name != NULL, NULL);
property_info =
_nm_setting_class_get_property_info(NM_SETTING_GET_CLASS(setting), property_name);
g_return_val_if_fail(property_info != NULL, NULL);
nm_assert(property_info->property_type);
nm_assert(
g_variant_type_string_is_valid((const char *) property_info->property_type->dbus_type));
return property_info->property_type->dbus_type;
}
gboolean
_nm_setting_get_property(NMSetting *setting, const char *property_name, GValue *value)
{
const NMSettInfoSetting *sett_info;
const NMSettInfoProperty *property_info;
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_setting_class_get_sett_info(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;
}
property_info = _nm_sett_info_setting_get_property_info(sett_info, property_name);
if (!property_info || !property_info->param_spec) {
g_value_unset(value);
return FALSE;
}
g_value_init(value, property_info->param_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);
}
static void
duplicate_copy_properties(const NMSettInfoSetting *sett_info, NMSetting *src, NMSetting *dst)
{
gboolean frozen = FALSE;
guint16 i;
if (sett_info->detail.gendata_info) {
GenData *gendata = _gendata_hash(src, FALSE);
nm_assert(!_gendata_hash(dst, FALSE));
if (gendata && g_hash_table_size(gendata->hash) > 0) {
GHashTableIter iter;
GHashTable *h = _gendata_hash(dst, 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));
}
}
}
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
if (!property_info->param_spec)
continue;
nm_assert(!NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_CONSTRUCT_ONLY));
if (property_info->property_type == &nm_sett_info_propert_type_setting_name)
continue;
nm_assert(NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE));
if (!frozen) {
g_object_freeze_notify(G_OBJECT(dst));
frozen = TRUE;
}
_gobject_copy_property(G_OBJECT(src),
G_OBJECT(dst),
property_info->param_spec->name,
G_PARAM_SPEC_VALUE_TYPE(property_info->param_spec));
}
if (frozen)
g_object_thaw_notify(G_OBJECT(dst));
}
/**
* 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;
NMSettingClass *klass;
NMSetting *dst;
g_return_val_if_fail(NM_IS_SETTING(setting), NULL);
klass = NM_SETTING_GET_CLASS(setting);
nm_assert(NM_IS_SETTING_CLASS(klass));
nm_assert(klass->duplicate_copy_properties);
dst = g_object_new(G_TYPE_FROM_CLASS(klass), NULL);
sett_info = _nm_setting_class_get_sett_info(klass);
nm_assert(sett_info);
klass->duplicate_copy_properties(sett_info, setting, dst);
return dst;
}
/**
* 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;
}
gboolean
_nm_setting_should_compare_secret_property(NMSetting *setting,
NMSetting *other,
const char *secret_name,
NMSettingCompareFlags flags)
{
NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
nm_assert(NM_IS_SETTING(setting));
nm_assert(!other || G_OBJECT_TYPE(setting) == G_OBJECT_TYPE(other));
/* secret_name must be a valid secret for @setting. */
nm_assert(nm_setting_get_secret_flags(setting, secret_name, NULL, NULL));
if (!NM_FLAGS_ANY(flags,
NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS
| NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS))
return TRUE;
nm_setting_get_secret_flags(setting, secret_name, &a_secret_flags, NULL);
if (other) {
if (!nm_setting_get_secret_flags(other, secret_name, &b_secret_flags, NULL)) {
/* secret-name may not be a valid secret for @other. That is fine, we ignore that
* and treat @b_secret_flags as NM_SETTING_SECRET_FLAG_NONE.
*
* This can happen with VPN secrets, where the caller knows that @secret_name
* is a secret for setting, but it may not be a secret for @other. Accept that.
*
* Mark @other as missing. */
other = NULL;
}
}
/* when @setting has the secret-flags that should be ignored,
* we skip the comparison if:
*
* - @other is not present,
* - @other does not have a secret named @secret_name
* - @other also has the secret flat to be ignored.
*
* This makes the check symmetric (aside the fact that @setting must
* have the secret while @other may not -- which is asymmetric). */
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
&& NM_FLAGS_HAS(a_secret_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)
&& (!other || NM_FLAGS_HAS(b_secret_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)))
return FALSE;
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
&& NM_FLAGS_HAS(a_secret_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)
&& (!other || NM_FLAGS_HAS(b_secret_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)))
return FALSE;
return TRUE;
}
/*****************************************************************************/
gboolean
_nm_setting_compare_flags_check(const GParamSpec *param_spec,
NMSettingCompareFlags flags,
NMSetting *set_a,
NMSetting *set_b)
{
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_FUZZY)
&& NM_FLAGS_ANY(param_spec->flags, NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))
return FALSE;
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
&& !NM_FLAGS_HAS(param_spec->flags, NM_SETTING_PARAM_INFERRABLE))
return FALSE;
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)
&& NM_FLAGS_HAS(param_spec->flags, NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
return FALSE;
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
&& NM_FLAGS_HAS(param_spec->flags, NM_SETTING_PARAM_SECRET))
return FALSE;
if (NM_FLAGS_HAS(param_spec->flags, NM_SETTING_PARAM_SECRET)
&& !_nm_setting_should_compare_secret_property(set_a, set_b, param_spec->name, flags))
return FALSE;
return TRUE;
}
NMTernary
_nm_setting_property_compare_fcn_ignore(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
{
return NM_TERNARY_DEFAULT;
}
NMTernary
_nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
{
gconstpointer p_a;
gconstpointer p_b;
nm_assert(NM_IN_SET(property_info->property_type->to_dbus_fcn,
_nm_setting_property_to_dbus_fcn_direct,
_nm_setting_property_to_dbus_fcn_direct_mac_address));
if (!property_info->param_spec)
return nm_assert_unreachable_val(NM_TERNARY_DEFAULT);
if (!_nm_setting_compare_flags_check(property_info->param_spec, flags, set_a, set_b))
return NM_TERNARY_DEFAULT;
if (!set_b)
return TRUE;
p_a = _nm_setting_get_private_field(set_a, sett_info, property_info);
p_b = _nm_setting_get_private_field(set_b, sett_info, property_info);
switch (property_info->property_type->direct_type) {
case NM_VALUE_TYPE_BOOL:
return *((const bool *) p_a) == *((const bool *) p_b);
case NM_VALUE_TYPE_INT32:
return *((const gint32 *) p_a) == *((const gint32 *) p_b);
case NM_VALUE_TYPE_UINT32:
return *((const guint32 *) p_a) == *((const guint32 *) p_b);
case NM_VALUE_TYPE_INT64:
return *((const gint64 *) p_a) == *((const gint64 *) p_b);
case NM_VALUE_TYPE_UINT64:
return *((const guint64 *) p_a) == *((const guint64 *) p_b);
case NM_VALUE_TYPE_ENUM:
return *((const int *) p_a) == *((const int *) p_b);
case NM_VALUE_TYPE_FLAGS:
return *((const guint *) p_a) == *((const guint *) p_b);
case NM_VALUE_TYPE_STRING:
return nm_streq0(*((const char *const *) p_a), *((const char *const *) p_b));
case NM_VALUE_TYPE_BYTES:
return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b));
case NM_VALUE_TYPE_STRV:
return nm_strvarray_equal(((const NMValueStrv *) p_a)->arr,
((const NMValueStrv *) p_b)->arr);
default:
return nm_assert_unreachable_val(TRUE);
}
}
NMTernary
_nm_setting_property_compare_fcn_default(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
{
nm_assert(property_info->property_type->direct_type == NM_VALUE_TYPE_NONE);
if (!property_info->param_spec)
return nm_assert_unreachable_val(NM_TERNARY_DEFAULT);
if (!_nm_setting_compare_flags_check(property_info->param_spec, flags, set_a, set_b))
return NM_TERNARY_DEFAULT;
if (!set_b)
return TRUE;
{
gs_unref_variant GVariant *value1 = NULL;
gs_unref_variant GVariant *value2 = NULL;
value1 = property_to_dbus(sett_info,
property_info,
con_a,
set_a,
NM_CONNECTION_SERIALIZE_ALL,
NULL,
TRUE);
value2 = property_to_dbus(sett_info,
property_info,
con_b,
set_b,
NM_CONNECTION_SERIALIZE_ALL,
NULL,
TRUE);
return nm_property_compare(value1, value2) == 0;
}
}
static NMTernary
_compare_property(const NMSettInfoSetting *sett_info,
const NMSettInfoProperty *property_info,
NMConnection *con_a,
NMSetting *set_a,
NMConnection *con_b,
NMSetting *set_b,
NMSettingCompareFlags flags)
{
NMTernary compare_result;
nm_assert(sett_info);
nm_assert(NM_IS_SETTING_CLASS(sett_info->setting_class));
nm_assert(property_info);
nm_assert(NM_SETTING_GET_CLASS(set_a) == sett_info->setting_class);
nm_assert(!set_b || NM_SETTING_GET_CLASS(set_b) == sett_info->setting_class);
compare_result = property_info->property_type
->compare_fcn(sett_info, property_info, con_a, set_a, con_b, set_b, flags);
nm_assert_is_ternary(compare_result);
/* check that the inferable flag and the GObject property flag corresponds. */
nm_assert(!NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE) || !property_info->param_spec
|| NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_INFERRABLE)
|| compare_result == NM_TERNARY_DEFAULT);
#if NM_MORE_ASSERTS > 10
/* assert that compare_fcn() is symeric. */
nm_assert(
!set_b
|| compare_result
== property_info->property_type
->compare_fcn(sett_info, property_info, con_b, set_b, con_a, set_a, flags));
#endif
return compare_result;
}
/**
* 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)
{
return _nm_setting_compare(NULL, a, NULL, b, flags);
}
gboolean
_nm_setting_compare(NMConnection *con_a,
NMSetting *a,
NMConnection *con_b,
NMSetting *b,
NMSettingCompareFlags flags)
{
const NMSettInfoSetting *sett_info;
guint16 i;
g_return_val_if_fail(NM_IS_SETTING(a), FALSE);
g_return_val_if_fail(NM_IS_SETTING(b), FALSE);
nm_assert(!con_a || NM_IS_CONNECTION(con_a));
nm_assert(!con_b || NM_IS_CONNECTION(con_b));
/* First check that both have the same type */
if (G_OBJECT_TYPE(a) != G_OBJECT_TYPE(b))
return FALSE;
sett_info = _nm_setting_class_get_sett_info(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_hashtable_equal(a_gendata ? a_gendata->hash : NULL,
b_gendata ? b_gendata->hash : NULL,
TRUE,
g_variant_equal);
}
for (i = 0; i < sett_info->property_infos_len; i++) {
if (_compare_property(sett_info, &sett_info->property_infos[i], con_a, a, con_b, b, flags)
== NM_TERNARY_FALSE)
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)
{
return _nm_setting_diff(NULL, a, NULL, b, flags, invert_results, results);
}
gboolean
_nm_setting_diff(NMConnection *con_a,
NMSetting *a,
NMConnection *con_b,
NMSetting *b,
NMSettingCompareFlags flags,
gboolean invert_results,
GHashTable **results)
{
const NMSettInfoSetting *sett_info;
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;
guint16 i;
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);
}
nm_assert(!con_a || NM_IS_CONNECTION(con_a));
nm_assert(!con_b || NM_IS_CONNECTION(con_b));
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_setting_class_get_sett_info(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++) {
NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN;
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
NMTernary compare_result;
GParamSpec *prop_spec;
compare_result = _compare_property(sett_info, property_info, con_a, a, con_b, b, flags);
if (compare_result == NM_TERNARY_DEFAULT)
continue;
if (NM_FLAGS_ANY(flags,
NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS
| NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
&& b && compare_result == NM_TERNARY_FALSE) {
/* we have setting @b and the property is not the same. But we also are instructed
* to ignore secrets based on the flags.
*
* Note that compare_property() called with two settings will ignore secrets
* based on the flags, but it will do so if *both* settings have the flag we
* look for. So that is symmetric behavior and good.
*
* But for the purpose of diff(), we do a asymmetric comparison because and
* we want to skip testing the property if setting @a alone indicates to do
* so.
*
* We need to double-check whether the property should be ignored by
* looking at @a alone. */
if (_compare_property(sett_info, property_info, con_a, a, NULL, NULL, flags)
== NM_TERNARY_DEFAULT)
continue;
}
compared_any = TRUE;
prop_spec = property_info->param_spec;
if (b) {
if (compare_result == NM_TERNARY_FALSE) {
if (prop_spec) {
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 (!NM_FLAGS_HAS(flags,
NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)) {
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
r |= a_result | b_result;
}
} 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 {
if (prop_spec) {
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);
} else
r |= a_result;
}
if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
diff_found = TRUE;
_setting_diff_add_result(*results, property_info->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;
}
}
static void
enumerate_values(const NMSettInfoProperty *property_info,
NMSetting *setting,
NMSettingValueIterFn func,
gpointer user_data)
{
GValue value = G_VALUE_INIT;
if (!property_info->param_spec)
return;
g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(property_info->param_spec));
g_object_get_property(G_OBJECT(setting), property_info->param_spec->name, &value);
func(setting,
property_info->param_spec->name,
&value,
property_info->param_spec->flags,
user_data);
g_value_unset(&value);
}
/**
* 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;
guint16 j;
g_return_if_fail(NM_IS_SETTING(setting));
g_return_if_fail(func != NULL);
sett_info = _nm_setting_class_get_sett_info(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_option_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 (j = 0; j < sett_info->property_infos_len; j++) {
NM_SETTING_GET_CLASS(setting)->enumerate_values(
_nm_sett_info_property_info_get_sorted(sett_info, j),
setting,
func,
user_data);
}
}
static gboolean
aggregate(NMSetting *setting, int type_i, gpointer arg)
{
NMConnectionAggregateType type = type_i;
const NMSettInfoSetting *sett_info;
guint16 i;
nm_assert(NM_IN_SET(type,
NM_CONNECTION_AGGREGATE_ANY_SECRETS,
NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS));
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
GParamSpec *prop_spec = property_info->param_spec;
nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
NMSettingSecretFlags secret_flags;
if (!prop_spec || !NM_FLAGS_HAS(prop_spec->flags, NM_SETTING_PARAM_SECRET)) {
nm_assert(!nm_setting_get_secret_flags(setting, property_info->name, NULL, NULL));
continue;
}
/* for the moment, all aggregate types only care about secrets. */
nm_assert(nm_setting_get_secret_flags(setting, property_info->name, NULL, NULL));
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_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)
{
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);
return NM_SETTING_GET_CLASS(setting)->aggregate(setting, type, arg);
}
static gboolean
clear_secrets(const NMSettInfoSetting *sett_info,
const NMSettInfoProperty *property_info,
NMSetting *setting,
NMSettingClearSecretsWithFlagsFn func,
gpointer user_data)
{
NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
GParamSpec *param_spec = property_info->param_spec;
if (!param_spec)
return FALSE;
if (!NM_FLAGS_HAS(param_spec->flags, NM_SETTING_PARAM_SECRET))
return FALSE;
if (func) {
if (!nm_setting_get_secret_flags(setting, param_spec->name, &flags, NULL))
nm_assert_not_reached();
if (!func(setting, param_spec->name, flags, user_data))
return FALSE;
} else
nm_assert(nm_setting_get_secret_flags(setting, param_spec->name, NULL, NULL));
{
nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
g_value_init(&value, param_spec->value_type);
g_object_get_property(G_OBJECT(setting), param_spec->name, &value);
if (g_param_value_defaults(param_spec, &value))
return FALSE;
g_param_value_set_default(param_spec, &value);
g_object_set_property(G_OBJECT(setting), param_spec->name, &value);
}
return TRUE;
}
/**
* _nm_setting_clear_secrets:
* @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(NMSetting *setting,
NMSettingClearSecretsWithFlagsFn func,
gpointer user_data)
{
const NMSettInfoSetting *sett_info;
gboolean changed = FALSE;
NMSettingClass *klass;
guint16 i;
g_return_val_if_fail(NM_IS_SETTING(setting), FALSE);
klass = NM_SETTING_GET_CLASS(setting);
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
for (i = 0; i < sett_info->property_infos_len; i++) {
changed |= klass->clear_secrets(sett_info,
&sett_info->property_infos[i],
setting,
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 NMSettInfoSetting *sett_info;
const NMSettInfoProperty *property_info;
gboolean is_modified;
sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
property_info = _nm_sett_info_setting_get_property_info(sett_info, key);
if (!property_info) {
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;
}
if (!property_info->param_spec
|| !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
/* Silently ignore non-secrets */
return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
}
if (!_property_set_from_dbus(sett_info,
property_info,
setting,
NULL,
value,
NM_SETTING_PARSE_FLAGS_BEST_EFFORT,
&is_modified,
NULL)) {
/* Silently ignore errors. */
}
return is_modified ? NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED
: NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
}
/**
* _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);
nm_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
for_each_secret(NMSetting *setting,
const char *secret_name,
GVariant *val,
gboolean remove_non_secrets,
_NMConnectionForEachSecretFunc callback,
gpointer callback_data,
GVariantBuilder *setting_builder)
{
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
if (!nm_setting_get_secret_flags(setting, secret_name, &secret_flags, NULL)) {
if (!remove_non_secrets)
g_variant_builder_add(setting_builder, "{sv}", secret_name, val);
return;
}
if (callback(secret_flags, callback_data))
g_variant_builder_add(setting_builder, "{sv}", secret_name, val);
}
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_setting_class_get_property_info(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_setting_class_get_property_info(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, NULL);
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);
}
static GVariant *
_nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
{
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;
}
const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
.compare_fcn = _nm_setting_property_compare_fcn_ignore,
.to_dbus_fcn =
_nm_setting_get_deprecated_virtual_interface_name, );
const NMSettInfoPropertType nm_sett_info_propert_type_setting_name =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_ignore,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_ignore,
.from_dbus_is_full = TRUE,
.compare_fcn = _nm_setting_property_compare_fcn_ignore);
const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_i =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
G_VARIANT_TYPE_INT32,
/* No functions set. This property type is to silently ignore the value on D-Bus. */
.compare_fcn = _nm_setting_property_compare_fcn_ignore);
const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_u =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
G_VARIANT_TYPE_UINT32,
/* No functions set. This property type is to silently ignore the value on D-Bus. */
.compare_fcn = _nm_setting_property_compare_fcn_ignore);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_boolean =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BOOLEAN,
.direct_type = NM_VALUE_TYPE_BOOL,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_int32 =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32,
.direct_type = NM_VALUE_TYPE_INT32,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint32 =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_UINT32,
.direct_type = NM_VALUE_TYPE_UINT32,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_int64 =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT64,
.direct_type = NM_VALUE_TYPE_INT64,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint64 =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_UINT64,
.direct_type = NM_VALUE_TYPE_UINT64,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_string =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
.direct_type = NM_VALUE_TYPE_STRING,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BYTESTRING,
.direct_type = NM_VALUE_TYPE_BYTES,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_strv =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING_ARRAY,
.direct_type = NM_VALUE_TYPE_STRV,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32,
.direct_type = NM_VALUE_TYPE_ENUM,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_flags =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_UINT32,
.direct_type = NM_VALUE_TYPE_FLAGS,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
.from_dbus_is_full = TRUE,
.from_dbus_direct_allow_transform = TRUE);
const NMSettInfoPropertType nm_sett_info_propert_type_direct_mac_address =
NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
G_VARIANT_TYPE_BYTESTRING,
.direct_type = NM_VALUE_TYPE_STRING,
.compare_fcn = _nm_setting_property_compare_fcn_direct,
.to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct_mac_address,
.from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct_mac_address);
/*****************************************************************************/
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_option_hash(NMSetting *setting, gboolean create_if_necessary)
{
GenData *gendata;
gendata = _gendata_hash(setting, create_if_necessary);
return gendata ? gendata->hash : NULL;
}
void
_nm_setting_option_notify(NMSetting *setting, gboolean names_changed)
{
GenData *gendata;
gendata = _gendata_hash(setting, FALSE);
if (!gendata)
goto out;
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, currently there is no 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. */
out:
_nm_setting_emit_property_changed(setting);
}
guint
_nm_setting_option_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_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_option_get_all_names:
* @setting: the #NMSetting
* @out_len: (allow-none) (out):
*
* Gives the name of all set options.
*
* 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
* by the next operation.
*
* Since: 1.26
**/
const char *const *
nm_setting_option_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_option_get_all(setting, &names, NULL);
NM_SET_OUT(out_len, len);
return names;
}
gboolean
_nm_setting_option_clear(NMSetting *setting, const char *optname)
{
GHashTable *ht;
nm_assert(NM_IS_SETTING(setting));
nm_assert(nm_str_not_empty(optname));
ht = _nm_setting_option_hash(setting, FALSE);
if (!ht)
return FALSE;
return g_hash_table_remove(ht, optname);
}
/**
* nm_setting_option_clear_by_name:
* @setting: the #NMSetting
* @predicate: (allow-none) (scope call): the predicate for which names
* should be clear.
* If the predicate returns %TRUE for an option name, the option
* gets removed. If %NULL, all options will be removed.
*
* Since: 1.26
*/
void
nm_setting_option_clear_by_name(NMSetting *setting, NMUtilsPredicateStr predicate)
{
GHashTable *hash;
GHashTableIter iter;
const char *name;
gboolean changed = FALSE;
g_return_if_fail(NM_IS_SETTING(setting));
hash = _nm_setting_option_hash(NM_SETTING(setting), FALSE);
if (!hash)
return;
if (!predicate) {
changed = (g_hash_table_size(hash) > 0);
if (changed)
g_hash_table_remove_all(hash);
} else {
g_hash_table_iter_init(&iter, hash);
while (g_hash_table_iter_next(&iter, (gpointer *) &name, NULL)) {
if (predicate(name)) {
g_hash_table_iter_remove(&iter);
changed = TRUE;
}
}
}
if (changed)
_nm_setting_option_notify(setting, TRUE);
}
/*****************************************************************************/
/**
* nm_setting_option_get:
* @setting: the #NMSetting
* @opt_name: the option name to request.
*
* Returns: (transfer none): the #GVariant or %NULL if the option
* is not set.
*
* Since: 1.26.
*/
GVariant *
nm_setting_option_get(NMSetting *setting, const char *opt_name)
{
GenData *gendata;
g_return_val_if_fail(NM_IS_SETTING(setting), FALSE);
g_return_val_if_fail(opt_name, FALSE);
gendata = _gendata_hash(setting, FALSE);
return gendata ? g_hash_table_lookup(gendata->hash, opt_name) : NULL;
}
/**
* nm_setting_option_get_boolean:
* @setting: the #NMSetting
* @opt_name: the option to get
* @out_value: (allow-none) (out): the optional output value.
* If the option is unset, %FALSE will be returned.
*
* Returns: %TRUE if @opt_name is set to a boolean variant.
*
* Since: 1.26
*/
gboolean
nm_setting_option_get_boolean(NMSetting *setting, const char *opt_name, gboolean *out_value)
{
GVariant *v;
v = nm_setting_option_get(NM_SETTING(setting), opt_name);
if (v && g_variant_is_of_type(v, G_VARIANT_TYPE_BOOLEAN)) {
NM_SET_OUT(out_value, g_variant_get_boolean(v));
return TRUE;
}
NM_SET_OUT(out_value, FALSE);
return FALSE;
}
/**
* nm_setting_option_get_uint32:
* @setting: the #NMSetting
* @opt_name: the option to get
* @out_value: (allow-none) (out): the optional output value.
* If the option is unset, 0 will be returned.
*
* Returns: %TRUE if @opt_name is set to a uint32 variant.
*
* Since: 1.26
*/
gboolean
nm_setting_option_get_uint32(NMSetting *setting, const char *opt_name, guint32 *out_value)
{
GVariant *v;
v = nm_setting_option_get(NM_SETTING(setting), opt_name);
if (v && g_variant_is_of_type(v, G_VARIANT_TYPE_UINT32)) {
NM_SET_OUT(out_value, g_variant_get_uint32(v));
return TRUE;
}
NM_SET_OUT(out_value, 0);
return FALSE;
}
/**
* nm_setting_option_set:
* @setting: the #NMSetting
* @opt_name: the option name to set
* @variant: (allow-none): the variant to set.
*
* If @variant is %NULL, this clears the option if it is set.
* Otherwise, @variant is set as the option. If @variant is
* a floating reference, it will be consumed.
*
* Note that not all setting types support options. It is a bug
* setting a variant to a setting that doesn't support it.
* Currently, only #NMSettingEthtool supports it.
*
* Since: 1.26
*/
void
nm_setting_option_set(NMSetting *setting, const char *opt_name, GVariant *variant)
{
GVariant *old_variant;
gboolean changed_name;
gboolean changed_value;
GHashTable *hash;
g_return_if_fail(NM_IS_SETTING(setting));
g_return_if_fail(opt_name);
hash = _nm_setting_option_hash(setting, variant != NULL);
if (!variant) {
if (hash) {
if (g_hash_table_remove(hash, opt_name))
_nm_setting_option_notify(setting, TRUE);
}
return;
}
/* Currently, it is a bug setting any option, unless the setting type supports it.
* And currently, only NMSettingEthtool supports it.
*
* In the future, more setting types may support it. Or we may relax this so
* that options can be attached to all setting types (to indicate "unsupported"
* settings for forward compatibility).
*
* As it is today, internal code will only add gendata options to NMSettingEthtool,
* and there exists not public API to add such options. Still, it is permissible
* to call get(), clear() and set(variant=NULL) also on settings that don't support
* it, as these operations don't add options.
*/
g_return_if_fail(
_nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting))->detail.gendata_info);
old_variant = g_hash_table_lookup(hash, opt_name);
changed_name = (old_variant == NULL);
changed_value = changed_name || !g_variant_equal(old_variant, variant);
/* We always want to replace the variant, even if it has
* the same value according to g_variant_equal(). The reason
* is that we want to take a reference on @variant, because
* that is what the user might expect. */
g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(variant));
if (changed_value)
_nm_setting_option_notify(setting, changed_name);
}
/**
* nm_setting_option_set_boolean:
* @setting: the #NMSetting
* @value: the value to set.
*
* Like nm_setting_option_set() to set a boolean GVariant.
*
* Since: 1.26
*/
void
nm_setting_option_set_boolean(NMSetting *setting, const char *opt_name, gboolean value)
{
GVariant *old_variant;
gboolean changed_name;
gboolean changed_value;
GHashTable *hash;
g_return_if_fail(NM_IS_SETTING(setting));
g_return_if_fail(opt_name);
value = (!!value);
hash = _nm_setting_option_hash(setting, TRUE);
old_variant = g_hash_table_lookup(hash, opt_name);
changed_name = (old_variant == NULL);
changed_value = changed_name
|| (!g_variant_is_of_type(old_variant, G_VARIANT_TYPE_BOOLEAN)
|| g_variant_get_boolean(old_variant) != value);
g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(g_variant_new_boolean(value)));
if (changed_value)
_nm_setting_option_notify(setting, changed_name);
}
/**
* nm_setting_option_set_uint32:
* @setting: the #NMSetting
* @value: the value to set.
*
* Like nm_setting_option_set() to set a uint32 GVariant.
*
* Since: 1.26
*/
void
nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 value)
{
GVariant *old_variant;
gboolean changed_name;
gboolean changed_value;
GHashTable *hash;
g_return_if_fail(NM_IS_SETTING(setting));
g_return_if_fail(opt_name);
hash = _nm_setting_option_hash(setting, TRUE);
old_variant = g_hash_table_lookup(hash, opt_name);
changed_name = (old_variant == NULL);
changed_value = changed_name
|| (!g_variant_is_of_type(old_variant, G_VARIANT_TYPE_UINT32)
|| g_variant_get_uint32(old_variant) != value);
g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(g_variant_new_uint32(value)));
if (changed_value)
_nm_setting_option_notify(setting, changed_name);
}
/*****************************************************************************/
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
nm_setting_init(NMSetting *setting)
{}
static void
constructed(GObject *object)
{
NMSetting *self = NM_SETTING(object);
NMSettingClass *klass = NM_SETTING_GET_CLASS(self);
/* we don't support that NMSetting subclasses override constructed.
* They all must have no G_PARAM_CONSTRUCT/G_PARAM_CONSTRUCT_ONLY
* properties, otherwise the automatism of _init_direct() needs
* careful adjustment. */
nm_assert(G_OBJECT_CLASS(klass)->constructed == constructed);
/* we always initialize the defaults of the (direct) properties. Note that:
*
* - we don't use CONSTRUCT properties, because they have an overhead during
* each object creation. Via _init_direct() we can do it more efficiently.
*
* - we always call this, because we want to get all default values right.
* We even call this for NMSetting subclasses that (historically) are not
* yet aware of this happening.
*/
_init_direct(self);
G_OBJECT_CLASS(nm_setting_parent_class)->constructed(object);
}
static void
finalize(GObject *object)
{
NMSetting *self = NM_SETTING(object);
NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE(self);
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);
_finalize_direct(self);
}
static void
nm_setting_class_init(NMSettingClass *setting_class)
{
GObjectClass *object_class = G_OBJECT_CLASS(setting_class);
g_type_class_add_private(setting_class, sizeof(NMSettingPrivate));
object_class->constructed = constructed;
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->clear_secrets = clear_secrets;
setting_class->for_each_secret = for_each_secret;
setting_class->duplicate_copy_properties = duplicate_copy_properties;
setting_class->enumerate_values = enumerate_values;
setting_class->aggregate = aggregate;
setting_class->init_from_dbus = init_from_dbus;
/**
* 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".
**/
obj_properties[PROP_NAME] = g_param_spec_string(NM_SETTING_NAME,
"",
"",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}