nm-setting.c 78.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 * Copyright 2007 - 2011 Red Hat, Inc.
 * Copyright 2007 - 2008 Novell, Inc.
 */

23
#include "nm-default.h"
24

25
#include "nm-setting.h"
26 27 28

#include <string.h>

29 30
#include "nm-setting-private.h"
#include "nm-utils.h"
31
#include "nm-core-internal.h"
32
#include "nm-utils-private.h"
33
#include "nm-property-compare.h"
34

35 36 37 38 39 40 41 42 43
#include "nm-setting-connection.h"
#include "nm-setting-bond.h"
#include "nm-setting-bridge.h"
#include "nm-setting-bridge-port.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-team.h"
#include "nm-setting-team-port.h"
#include "nm-setting-vpn.h"

44 45 46 47 48 49 50 51 52 53 54 55
/**
 * 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.
 */

56
/*****************************************************************************/
57

58 59 60 61 62 63
typedef struct {
	GHashTable *hash;
	const char **names;
	GVariant **values;
} GenData;

64 65 66
typedef struct {
	const char *name;
	GType type;
67
	NMSettingPriority priority;
68 69 70 71 72 73 74 75 76
} SettingInfo;

enum {
	PROP_0,
	PROP_NAME,

	PROP_LAST
};

77
typedef struct {
78
	GenData *gendata;
79 80
} NMSettingPrivate;

81
G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
82 83 84

#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))

85
/*****************************************************************************/
86

87 88 89 90
static GenData *_gendata_hash (NMSetting *setting, gboolean create_if_necessary);

/*****************************************************************************/

91
static NMSettingPriority
92 93
_get_base_type_priority (const NMMetaSettingInfo *setting_info,
                         GType gtype)
94
{
95 96 97 98 99
	/* Historical oddity: PPPoE is a base-type even though it's not
	 * priority 1.  It needs to be sorted *after* lower-level stuff like
	 * Wi-Fi security or 802.1x for secrets, but it's still allowed as a
	 * base type.
	 */
100

101 102 103 104 105 106 107
	if (setting_info) {
		if (   NM_IN_SET (setting_info->setting_priority,
		                  NM_SETTING_PRIORITY_HW_BASE,
		                  NM_SETTING_PRIORITY_HW_NON_BASE)
		    || gtype == NM_TYPE_SETTING_PPPOE)
			return setting_info->setting_priority;
	}
108

109
	return NM_SETTING_PRIORITY_INVALID;
110 111
}

112
NMSettingPriority
113 114
_nm_setting_get_setting_priority (NMSetting *setting)
{
115 116 117
	const NMMetaSettingInfo *setting_info;

	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);
118

119 120
	setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
	return setting_info ? setting_info->setting_priority : NM_SETTING_PRIORITY_INVALID;
121 122
}

123
NMSettingPriority
124
_nm_setting_type_get_base_type_priority (GType type)
125
{
126 127
	return _get_base_type_priority (nm_meta_setting_infos_by_gtype (type),
	                                type);
128 129
}

130
NMSettingPriority
131
_nm_setting_get_base_type_priority (NMSetting *setting)
132
{
133 134 135 136
	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);

	return _get_base_type_priority (NM_SETTING_GET_CLASS (setting)->setting_info,
	                                G_OBJECT_TYPE (setting));
137 138
}

139 140 141 142 143 144
/**
 * nm_setting_lookup_type:
 * @name: a setting name
 *
 * Returns the #GType of the setting's class for a given setting name.
 *
145 146
 * Returns: the #GType of the setting's class, or %G_TYPE_INVALID if
 *   @name is not recognized.
147
 **/
148
GType
149
nm_setting_lookup_type (const char *name)
150
{
151
	const NMMetaSettingInfo *setting_info;
152

153
	g_return_val_if_fail (name, G_TYPE_INVALID);
154

155 156
	setting_info = nm_meta_setting_infos_by_name (name);
	return setting_info ? setting_info->get_setting_gtype () : G_TYPE_INVALID;
157 158
}

159
int
160 161
_nm_setting_compare_priority (gconstpointer a, gconstpointer b)
{
162
	NMSettingPriority prio_a, prio_b;
163

164 165
	prio_a = _nm_setting_get_setting_priority ((NMSetting *) a);
	prio_b = _nm_setting_get_setting_priority ((NMSetting *) b);
166 167 168 169 170 171 172 173

	if (prio_a < prio_b)
		return -1;
	else if (prio_a == prio_b)
		return 0;
	return 1;
}

174
/*****************************************************************************/
175

176 177 178 179 180 181 182 183 184 185 186 187
gboolean
_nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_type)
{
	const char *port_type = NULL;
	gboolean found = TRUE;

	if (!slave_type)
		found = FALSE;
	else if (!strcmp (slave_type, NM_SETTING_BOND_SETTING_NAME))
		;
	else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME))
		port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME;
188 189
	else if (!strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
		port_type = NM_SETTING_OVS_PORT_SETTING_NAME;
190 191
	else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
		port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
192 193 194 195 196 197 198 199 200 201
	else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
		port_type = NM_SETTING_TEAM_PORT_SETTING_NAME;
	else
		found = FALSE;

	if (out_port_type)
		*out_port_type = port_type;
	return found;
}

202
/*****************************************************************************/
203

204 205
static const NMSettInfoProperty *
_nm_sett_info_property_find_in_array (const NMSettInfoProperty *properties, guint len, const char *name)
206
{
207
	guint i;
208

209 210 211
	for (i = 0; i < len; i++) {
		if (nm_streq (name, properties[i].name))
			return &properties[i];
212 213 214 215
	}
	return NULL;
}

216 217 218
void
_properties_override_add_struct (GArray *properties_override,
                                 const NMSettInfoProperty *prop_info)
219
{
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	nm_assert (properties_override);
	nm_assert (prop_info);
	nm_assert (prop_info->name || prop_info->param_spec);
	nm_assert (!prop_info->param_spec || !prop_info->name || nm_streq0 (prop_info->name, prop_info->param_spec->name));
	nm_assert (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
	                                                  properties_override->len,
	                                                  prop_info->name ?: prop_info->param_spec->name));

	nm_assert (!prop_info->from_dbus || prop_info->dbus_type);
	nm_assert (!prop_info->set_func || prop_info->dbus_type);

	g_array_append_vals (properties_override, prop_info, 1);

	if (!prop_info->name) {
		/* for convenience, allow omitting "name" if "param_spec" is given. */
		g_array_index (properties_override,
		               NMSettInfoProperty,
		               properties_override->len - 1).name = prop_info->param_spec->name;
238 239 240 241
	}
}

/**
242 243
 * _properties_override_add_dbus_only:
 * @properties_override: an array collecting the overrides
244
 * @property_name: the name of the property to override
245
 * @dbus_type: the type of the property (in its D-Bus representation)
246
 * @synth_func: (allow-none): function to call to synthesize a value for the property
247 248 249
 * @set_func: (allow-none): function to call to set the value of the property
 *
 * Registers a property named @property_name, which will be used in the D-Bus
250
 * serialization of objects of this setting type, but which does not correspond to
251 252
 * a #GObject property.
 *
253 254 255 256
 * When serializing a setting to D-Bus, @synth_func will be called to synthesize
 * a value for the property. (If it returns %NULL, no value will be added to the
 * serialization. If @synth_func is %NULL, the property will always be omitted
 * in the serialization.)
257 258
 *
 * When deserializing a D-Bus representation into a setting, if @property_name
259 260
 * is present, then @set_func will be called to set it. (If @set_func is %NULL
 * then the property will be ignored when deserializing.)
261 262
 */
void
263 264 265 266 267
_properties_override_add_dbus_only (GArray *properties_override,
                                    const char *property_name,
                                    const GVariantType *dbus_type,
                                    NMSettingPropertySynthFunc synth_func,
                                    NMSettingPropertySetFunc set_func)
268
{
269 270 271 272 273
	_properties_override_add (properties_override,
	                          .name = property_name,
	                          .dbus_type = dbus_type,
	                          .synth_func = synth_func,
	                          .set_func = set_func);
274 275 276
}

/**
277 278 279
 * _properties_override_add_override:
 * @properties_override: an array collecting the overrides
 * @param_spec: the name of the property to override
280 281 282 283 284
 * @dbus_type: the type of the property (in its D-Bus representation)
 * @get_func: (allow-none): function to call to get the value of the property
 * @set_func: (allow-none): function to call to set the value of the property
 * @not_set_func: (allow-none): function to call to indicate the property was not set
 *
285 286
 * Overrides the D-Bus representation of the #GObject property that shares the
 * same name as @param_spec.
287 288
 *
 * When serializing a setting to D-Bus, if @get_func is non-%NULL, then it will
289 290 291 292 293
 * be called to get the property's value. If it returns a #GVariant, the
 * property will be added to the hash, and if it returns %NULL, the property
 * will be omitted. (If @get_func is %NULL, the property will be read normally
 * with g_object_get_property(), and added to the hash if it is not the default
 * value.)
294
 *
295 296 297 298
 * When deserializing a D-Bus representation into a setting, if a value with
 * the name of @param_spec is present, then @set_func will be called to set it.
 * (If @set_func is %NULL then the property will be set normally with
 * g_object_set_property().)
299 300
 *
 * If @not_set_func is non-%NULL, then it will be called when deserializing a
301 302
 * representation that does NOT contain a value for the property. This can be used,
 * eg, if a new property needs to be initialized from some older deprecated property
303 304 305
 * when it is not present.
 */
void
306 307 308 309 310 311
_properties_override_add_override (GArray *properties_override,
                                   GParamSpec *param_spec,
                                   const GVariantType *dbus_type,
                                   NMSettingPropertyGetFunc get_func,
                                   NMSettingPropertySetFunc set_func,
                                   NMSettingPropertyNotSetFunc not_set_func)
312
{
313 314 315 316 317 318 319 320
	nm_assert (param_spec);

	_properties_override_add (properties_override,
	                          .param_spec = param_spec,
	                          .dbus_type = dbus_type,
	                          .get_func = get_func,
	                          .set_func = set_func,
	                          .not_set_func = not_set_func);
321 322 323
}

/**
324 325 326
 * _properties_override_add_transform:
 * @properties_override: an array collecting the overrides
 * @param_spec: the param spec of the property to transform.
327 328 329 330 331 332 333 334 335 336 337 338 339
 * @dbus_type: the type of the property (in its D-Bus representation)
 * @to_dbus: function to convert from object to D-Bus format
 * @from_dbus: function to convert from D-Bus to object format
 *
 * Indicates that @property on @setting_class does not have the same format as
 * its corresponding D-Bus representation, and so must be transformed when
 * serializing/deserializing.
 *
 * The transformation will also be used by nm_setting_compare(), meaning that
 * the underlying object property does not need to be of a type that
 * nm_property_compare() recognizes, as long as it recognizes @dbus_type.
 */
void
340 341 342 343 344
_properties_override_add_transform (GArray *properties_override,
                                    GParamSpec *param_spec,
                                    const GVariantType *dbus_type,
                                    NMSettingPropertyTransformToFunc to_dbus,
                                    NMSettingPropertyTransformFromFunc from_dbus)
345
{
346
	nm_assert (param_spec);
347

348 349 350 351 352
	_properties_override_add (properties_override,
	                          .param_spec = param_spec,
	                          .dbus_type = dbus_type,
	                          .to_dbus = to_dbus,
	                          .from_dbus = from_dbus);
353 354
}

355 356 357 358 359 360 361
static NMSettInfoSetting _sett_info_settings[_NM_META_SETTING_TYPE_NUM];

void
_nm_setting_class_commit_full (NMSettingClass *setting_class,
                               NMMetaSettingType meta_type,
                               const NMSettInfoSettDetail *detail,
                               GArray *properties_override)
362
{
363 364 365 366 367 368 369 370
	NMSettInfoSetting *sett_info;
	gs_free GParamSpec **property_specs = NULL;
	guint i, n_property_specs, override_len;

	nm_assert (NM_IS_SETTING_CLASS (setting_class));
	nm_assert (!setting_class->setting_info);

	nm_assert (meta_type < G_N_ELEMENTS (_sett_info_settings));
371

372 373 374 375 376 377 378 379 380 381 382
	sett_info = &_sett_info_settings[meta_type];

	nm_assert (!sett_info->setting_class);
	nm_assert (!sett_info->property_infos_len);
	nm_assert (!sett_info->property_infos);

	if (!properties_override) {
		override_len = 0;
		properties_override = _nm_sett_info_property_override_create_array ();
	} else
		override_len = properties_override->len;
383 384 385

	property_specs = g_object_class_list_properties (G_OBJECT_CLASS (setting_class),
	                                                 &n_property_specs);
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

#if NM_MORE_ASSERTS > 10
	/* assert that properties_override is constructed consistently. */
	for (i = 0; i < override_len; i++) {
		guint j;
		const NMSettInfoProperty *p = &g_array_index (properties_override, NMSettInfoProperty, i);

		nm_assert (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
		                                                  i,
		                                                  p->name));
		for (j = 0; j < n_property_specs; j++) {
			if (nm_streq (property_specs[j]->name, p->name)) {
				nm_assert (p->param_spec == property_specs[j]);
				break;
			}
401
		}
402
		nm_assert ((j == n_property_specs) == (p->param_spec == NULL));
403
	}
404 405 406 407 408
#endif

	for (i = 0; i < n_property_specs; i++) {
		const char *name = property_specs[i]->name;
		NMSettInfoProperty *p;
409

410 411 412 413 414 415 416 417 418 419
		if (_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
		                                           override_len,
		                                           name))
			continue;

		g_array_set_size (properties_override, properties_override->len + 1);
		p = &g_array_index (properties_override, NMSettInfoProperty, properties_override->len - 1);
		memset (p, 0, sizeof (*p));
		p->name = name;
		p->param_spec = property_specs[i];
420 421
	}

422 423 424 425 426 427 428 429 430 431
	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMSettInfoProperty, name) == 0);
	g_array_sort (properties_override, nm_strcmp_p);

	setting_class->setting_info = &nm_meta_setting_infos[meta_type];
	sett_info->setting_class = setting_class;
	if (detail)
		sett_info->detail = *detail;
	sett_info->property_infos_len = properties_override->len;
	sett_info->property_infos = (const NMSettInfoProperty *) g_array_free (properties_override,
	                                                                       properties_override->len == 0);
432 433
}

434 435
const NMSettInfoSetting *
_nm_sett_info_setting_get (NMSettingClass *setting_class)
436
{
437 438 439 440 441 442
	if (   NM_IS_SETTING_CLASS (setting_class)
	    && setting_class->setting_info) {
		nm_assert (setting_class->setting_info->meta_type < G_N_ELEMENTS (_sett_info_settings));
		return &_sett_info_settings[setting_class->setting_info->meta_type];
	}
	return NULL;
443 444
}

445 446 447
const NMSettInfoProperty *
_nm_sett_info_property_get (NMSettingClass *setting_class,
                            const char *property_name)
448
{
449 450 451
	const NMSettInfoSetting *sett_info = _nm_sett_info_setting_get (setting_class);
	const NMSettInfoProperty *property;
	gssize idx;
452

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
	if (!sett_info)
		return NULL;

	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMSettInfoProperty, name) == 0);
	idx = nm_utils_array_find_binary_search (sett_info->property_infos,
	                                         sizeof (NMSettInfoProperty),
	                                         sett_info->property_infos_len,
	                                         &property_name,
	                                         nm_strcmp_p_with_data,
	                                         NULL);

	if (idx < 0)
		return NULL;

	property = &sett_info->property_infos[idx];

	nm_assert (idx == 0 || strcmp (property[-1].name, property[0].name) < 0);
	nm_assert (idx == sett_info->property_infos_len - 1 || strcmp (property[0].name, property[1].name) < 0);

	return property;
473 474
}

475
/*****************************************************************************/
476

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
gboolean
_nm_setting_use_legacy_property (NMSetting *setting,
                                 GVariant *connection_dict,
                                 const char *legacy_property,
                                 const char *new_property)
{
	GVariant *setting_dict, *value;

	setting_dict = g_variant_lookup_value (connection_dict, nm_setting_get_name (NM_SETTING (setting)), NM_VARIANT_TYPE_SETTING);
	g_return_val_if_fail (setting_dict != NULL, FALSE);

	/* If the new property isn't set, we have to use the legacy property. */
	value = g_variant_lookup_value (setting_dict, new_property, NULL);
	if (!value) {
		g_variant_unref (setting_dict);
		return TRUE;
	}
	g_variant_unref (value);

	/* Otherwise, clients always prefer new properties sent from the daemon. */
	if (!_nm_utils_is_manager_process) {
		g_variant_unref (setting_dict);
		return FALSE;
	}

	/* The daemon prefers the legacy property if it exists. */
	value = g_variant_lookup_value (setting_dict, legacy_property, NULL);
	g_variant_unref (setting_dict);

	if (value) {
		g_variant_unref (value);
		return TRUE;
	} else
		return FALSE;
}

/*****************************************************************************/

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
static const GVariantType *
variant_type_for_gtype (GType type)
{
	if (type == G_TYPE_BOOLEAN)
		return G_VARIANT_TYPE_BOOLEAN;
	else if (type == G_TYPE_UCHAR)
		return G_VARIANT_TYPE_BYTE;
	else if (type == G_TYPE_INT)
		return G_VARIANT_TYPE_INT32;
	else if (type == G_TYPE_UINT)
		return G_VARIANT_TYPE_UINT32;
	else if (type == G_TYPE_INT64)
		return G_VARIANT_TYPE_INT64;
	else if (type == G_TYPE_UINT64)
		return G_VARIANT_TYPE_UINT64;
	else if (type == G_TYPE_STRING)
		return G_VARIANT_TYPE_STRING;
	else if (type == G_TYPE_DOUBLE)
		return G_VARIANT_TYPE_DOUBLE;
	else if (type == G_TYPE_STRV)
		return G_VARIANT_TYPE_STRING_ARRAY;
536 537 538 539 540 541
	else if (type == G_TYPE_BYTES)
		return G_VARIANT_TYPE_BYTESTRING;
	else if (g_type_is_a (type, G_TYPE_ENUM))
		return G_VARIANT_TYPE_INT32;
	else if (g_type_is_a (type, G_TYPE_FLAGS))
		return G_VARIANT_TYPE_UINT32;
542 543 544 545 546 547
	else
		g_assert_not_reached ();
}

static GVariant *
get_property_for_dbus (NMSetting *setting,
548
                       const NMSettInfoProperty *property,
549 550 551 552 553
                       gboolean ignore_default)
{
	GValue prop_value = { 0, };
	GVariant *dbus_value;

554 555 556 557
	if (property->get_func)
		return property->get_func (setting, property->name);
	else
		g_return_val_if_fail (property->param_spec != NULL, NULL);
558 559 560 561 562 563 564 565 566 567 568 569 570

	g_value_init (&prop_value, property->param_spec->value_type);
	g_object_get_property (G_OBJECT (setting), property->param_spec->name, &prop_value);

	if (ignore_default && g_param_value_defaults (property->param_spec, &prop_value)) {
		g_value_unset (&prop_value);
		return NULL;
	}

	if (property->to_dbus)
		dbus_value = property->to_dbus (&prop_value);
	else if (property->dbus_type)
		dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, property->dbus_type);
571 572 573 574
	else if (g_type_is_a (prop_value.g_type, G_TYPE_ENUM))
		dbus_value = g_variant_new_int32 (g_value_get_enum (&prop_value));
	else if (g_type_is_a (prop_value.g_type, G_TYPE_FLAGS))
		dbus_value = g_variant_new_uint32 (g_value_get_flags (&prop_value));
575
	else if (prop_value.g_type == G_TYPE_BYTES)
576
		dbus_value = nm_utils_gbytes_to_variant_ay (g_value_get_boxed (&prop_value));
577 578 579 580 581 582 583
	else
		dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, variant_type_for_gtype (prop_value.g_type));
	g_value_unset (&prop_value);

	return dbus_value;
}

584
static gboolean
585
set_property_from_dbus (const NMSettInfoProperty *property,
586 587
                        GVariant *src_value,
                        GValue *dst_value)
588
{
589
	g_return_val_if_fail (property->param_spec != NULL, FALSE);
590

591 592 593 594
	if (property->from_dbus) {
		if (!g_variant_type_equal (g_variant_get_type (src_value), property->dbus_type))
			return FALSE;

595
		property->from_dbus (src_value, dst_value);
596 597 598 599
	} else if (dst_value->g_type == G_TYPE_BYTES) {
		if (!g_variant_is_of_type (src_value, G_VARIANT_TYPE_BYTESTRING))
			return FALSE;

600
		_nm_utils_bytes_from_dbus (src_value, dst_value);
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
	} else {
		GValue tmp = G_VALUE_INIT;

		g_dbus_gvariant_to_gvalue (src_value, &tmp);
		if (G_VALUE_TYPE (&tmp) == G_VALUE_TYPE (dst_value))
			*dst_value = tmp;
		else {
			gboolean success;

			success = g_value_transform (&tmp, dst_value);
			g_value_unset (&tmp);
			if (!success)
				return FALSE;
		}
	}

	return TRUE;
618 619 620
}

/**
621
 * _nm_setting_to_dbus:
622
 * @setting: the #NMSetting
623
 * @connection: the #NMConnection containing @setting
624
 * @flags: hash flags, e.g. %NM_CONNECTION_SERIALIZE_ALL
625
 *
626 627 628
 * 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.
629
 *
630 631
 * Returns: (transfer none): a new floating #GVariant describing the setting's
 * properties
632
 **/
633
GVariant *
634
_nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags)
635
{
636
	NMSettingPrivate *priv;
637 638
	GVariantBuilder builder;
	GVariant *dbus_value;
639
	const NMSettInfoSetting *sett_info;
640 641
	guint n_properties, i;
	const char *const*gendata_keys;
642 643 644

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);

645 646
	priv = NM_SETTING_GET_PRIVATE (setting);

647
	g_variant_builder_init (&builder, NM_VARIANT_TYPE_SETTING);
648

649 650 651 652 653 654 655 656
	n_properties = _nm_setting_gendata_get_all (setting, &gendata_keys, NULL);
	for (i = 0; i < n_properties; i++) {
		g_variant_builder_add (&builder,
		                       "{sv}",
		                       gendata_keys[i],
		                       g_hash_table_lookup (priv->gendata->hash, gendata_keys[i]));
	}

657 658 659
	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
	for (i = 0; i < sett_info->property_infos_len; i++) {
		const NMSettInfoProperty *property = &sett_info->property_infos[i];
660 661
		GParamSpec *prop_spec = property->param_spec;

662 663 664 665 666 667 668 669 670 671
		if (!prop_spec) {
			if (!property->synth_func)
				continue;

			if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
				continue;
		} else {
			if (!(prop_spec->flags & G_PARAM_WRITABLE))
				continue;

672 673 674
			if (NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_GENDATA_BACKED))
				continue;

675 676 677 678 679 680 681 682 683 684 685
			if (   (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
			    && !_nm_utils_is_manager_process)
				continue;

			if (   (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
			    && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
				continue;

			if (   (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
			    && !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
				continue;
686
		}
687

688 689 690 691 692 693
		if (property->synth_func) {
			if (!(flags & NM_CONNECTION_SERIALIZE_NO_SYNTH))
				dbus_value = property->synth_func (setting, connection, property->name);
			else
				dbus_value = NULL;
		} else {
694
			dbus_value = get_property_for_dbus (setting, property, TRUE);
695 696
		}

697 698 699
		if (dbus_value) {
			/* Allow dbus_value to be either floating or not. */
			g_variant_take_ref (dbus_value);
700

701 702 703
			g_variant_builder_add (&builder, "{sv}", property->name, dbus_value);
			g_variant_unref (dbus_value);
		}
704 705
	}

706
	return g_variant_builder_end (&builder);
707 708 709
}

/**
710
 * _nm_setting_new_from_dbus:
711
 * @setting_type: the #NMSetting type which the hash contains properties for
712 713 714 715
 * @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.
716
 * @parse_flags: flags to determine behavior during parsing.
717
 * @error: location to store error, or %NULL
718 719
 *
 * Creates a new #NMSetting object and populates that object with the properties
720 721 722 723 724
 * 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.
725 726
 *
 * Returns: a new #NMSetting object populated with the properties from the
727
 * hash table, or %NULL if @setting_hash could not be deserialized.
728 729
 **/
NMSetting *
730
_nm_setting_new_from_dbus (GType setting_type,
731 732
                           GVariant *setting_dict,
                           GVariant *connection_dict,
733
                           NMSettingParseFlags parse_flags,
734
                           GError **error)
735
{
736 737
	gs_unref_object NMSetting *setting = NULL;
	gs_unref_hashtable GHashTable *keys = NULL;
738 739
	const NMSettInfoSetting *sett_info;
	guint i;
740 741

	g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
742 743
	g_return_val_if_fail (g_variant_is_of_type (setting_dict, NM_VARIANT_TYPE_SETTING), NULL);

744 745 746
	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));

747 748 749 750 751
	/* 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);
752

753 754 755 756 757
	/* 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.
	 */
758
	setting = (NMSetting *) g_object_new (setting_type, NULL);
759

760 761 762 763 764
	if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
		GVariantIter iter;
		GVariant *entry, *entry_key;
		char *key;

765
		keys = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
766 767 768 769 770 771 772 773

		g_variant_iter_init (&iter, setting_dict);
		while ((entry = g_variant_iter_next_value (&iter))) {
			entry_key = g_variant_get_child_value (entry, 0);
			key = g_strdup (g_variant_get_string (entry_key, NULL));
			g_variant_unref (entry_key);
			g_variant_unref (entry);

Lubomir Rintel's avatar
Lubomir Rintel committed
774
			if (!g_hash_table_add (keys, key)) {
775 776 777 778 779 780 781 782
				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;
			}
		}
	}

783
	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803

	if (sett_info->detail.gendata_info) {
		GHashTable *hash;
		GVariantIter iter;
		char *key;
		GVariant *val;

		hash = _gendata_hash (setting, TRUE)->hash;

		g_variant_iter_init (&iter, setting_dict);
		while (g_variant_iter_next (&iter, "{sv}", &key, &val)) {
			g_hash_table_insert (hash,
			                     key,
			                     val);
		}

		_nm_setting_gendata_notify (setting, TRUE);
		return g_steal_pointer (&setting);
	}

804 805
	for (i = 0; i < sett_info->property_infos_len; i++) {
		const NMSettInfoProperty *property = &sett_info->property_infos[i];
806 807
		gs_unref_variant GVariant *value = NULL;
		gs_free_error GError *local = NULL;
808 809 810 811 812

		if (property->param_spec && !(property->param_spec->flags & G_PARAM_WRITABLE))
			continue;

		value = g_variant_lookup_value (setting_dict, property->name, NULL);
813

814 815 816
		if (value && keys)
			g_hash_table_remove (keys, property->name);

817
		if (value && property->set_func) {
818

819
			if (!g_variant_type_equal (g_variant_get_type (value), property->dbus_type)) {
820 821 822
				/* for backward behavior, fail unless best-effort is chosen. */
				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
					continue;
823 824 825 826
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("can't set property of type '%s' from value of type '%s'"),
				             property->dbus_type ?
				                 g_variant_type_peek_string (property->dbus_type) :
Jiří Klimeš's avatar
Jiří Klimeš committed
827 828
				                 property->param_spec ?
				                     g_type_name (property->param_spec->value_type) : "(unknown)",
829 830 831 832 833
				             g_variant_get_type_string (value));
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}

834 835 836 837 838 839 840 841 842 843 844 845 846 847
			if (!property->set_func (setting,
			                         connection_dict,
			                         property->name,
			                         value,
			                         parse_flags,
			                         &local)) {
				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("failed to set property: %s"),
				             local->message);
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
848
		} else if (!value && property->not_set_func) {
849 850 851 852 853 854 855 856 857 858 859 860 861
			if (!property->not_set_func (setting,
			                             connection_dict,
			                             property->name,
			                             parse_flags,
			                             &local)) {
				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("failed to set property: %s"),
				             local->message);
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
862
		} else if (value && property->param_spec) {
863
			nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
864 865

			g_value_init (&object_value, property->param_spec->value_type);
866 867 868 869 870 871 872 873 874 875 876 877 878 879
			if (!set_property_from_dbus (property, value, &object_value)) {
				/* for backward behavior, fail unless best-effort is chosen. */
				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("can't set property of type '%s' from value of type '%s'"),
				             property->dbus_type ?
				                 g_variant_type_peek_string (property->dbus_type) :
				                 property->param_spec ?
				                     g_type_name (property->param_spec->value_type) : "(unknown)",
				             g_variant_get_type_string (value));
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
880

881 882 883 884 885 886 887 888 889
			if (!nm_g_object_set_property (G_OBJECT (setting), property->param_spec->name, &object_value, &local)) {
				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("can not set property: %s"),
				             local->message);
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
890
		}
891
	}
892

893 894 895 896 897 898 899 900 901 902 903 904
	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;
		}
905
	}
906

907
	return g_steal_pointer (&setting);
908 909
}

910 911 912 913 914 915 916 917 918 919 920 921 922 923
/**
 * 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)
{
924
	const NMSettInfoProperty *property;
925 926 927 928

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
	g_return_val_if_fail (property_name != NULL, NULL);

929
	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), property_name);
930 931 932 933 934 935 936 937
	g_return_val_if_fail (property != NULL, NULL);

	if (property->dbus_type)
		return property->dbus_type;
	else
		return variant_type_for_gtype (property->param_spec->value_type);
}

938 939 940
gboolean
_nm_setting_get_property (NMSetting *setting, const char *property_name, GValue *value)
{
941
	const NMSettInfoSetting *sett_info;
942 943 944 945 946 947
	GParamSpec *prop_spec;

	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
	g_return_val_if_fail (property_name, FALSE);
	g_return_val_if_fail (value, FALSE);

948
	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
949

950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
	if (sett_info->detail.gendata_info) {
		GVariant *variant;
		GenData *gendata = _gendata_hash (setting, FALSE);

		variant = gendata ? g_hash_table_lookup (gendata->hash, property_name) : NULL;

		if (!variant) {
			g_value_unset (value);
			return FALSE;
		}

		g_value_init (value, G_TYPE_VARIANT);
		g_value_set_variant (value, variant);
		return TRUE;
	}

	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
967 968 969 970 971 972 973 974 975 976
	if (!prop_spec) {
		g_value_unset (value);
		return FALSE;
	}

	g_value_init (value, prop_spec->value_type);
	g_object_get_property (G_OBJECT (setting), property_name, value);
	return TRUE;
}

977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
static void
duplicate_setting (NMSetting *setting,
                   const char *name,
                   const GValue *value,
                   GParamFlags flags,
                   gpointer user_data)
{
	if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE)
		g_object_set_property (G_OBJECT (user_data), name, value);
}

/**
 * nm_setting_duplicate:
 * @setting: the #NMSetting to duplicate
 *
 * Duplicates a #NMSetting.
 *
 * Returns: (transfer full): a new #NMSetting containing the same properties and values as the
 * source #NMSetting
 **/
NMSetting *
nm_setting_duplicate (NMSetting *setting)
{
1000
	const NMSettInfoSetting *sett_info;
1001 1002 1003 1004 1005 1006
	GObject *dup;

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);

	dup = g_object_new (G_OBJECT_TYPE (setting), NULL);

1007
	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
1008

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
	if (sett_info->detail.gendata_info) {
		GenData *gendata = _gendata_hash (setting, FALSE);

		if (   gendata
		    && g_hash_table_size (gendata->hash) > 0) {
			GHashTableIter iter;
			GHashTable *h = _gendata_hash (NM_SETTING (dup), TRUE)->hash;
			const char *key;
			GVariant *val;

			g_hash_table_iter_init (&iter, gendata->hash);
			while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
				g_hash_table_insert (h,
				                     g_strdup (key),
				                     g_variant_ref (val));
			}
		}
	} else {
		g_object_freeze_notify (dup);
		nm_setting_enumerate_values (setting, duplicate_setting, dup);
		g_object_thaw_notify (dup);
	}
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
	return NM_SETTING (dup);
}

/**
 * nm_setting_get_name:
 * @setting: the #NMSetting
 *
 * Returns the type name of the #NMSetting object
 *
 * Returns: a string containing the type name of the #NMSetting object,
 * like 'ppp' or 'wireless' or 'wired'.
 **/
const char *
nm_setting_get_name (NMSetting *setting)
{
1046
	const NMMetaSettingInfo *setting_info;
1047 1048

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
1049 1050 1051

	setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
	return setting_info ? setting_info->setting_name : NULL;
1052 1053 1054 1055 1056
}

/**
 * nm_setting_verify:
 * @setting: the #NMSetting to verify
1057 1058
 * @connection: (allow-none): the #NMConnection that @setting came from, or
 *   %NULL if @setting is being verified in isolation.
1059 1060 1061
 * @error: location to store error, or %NULL
 *
 * Validates the setting.  Each setting's properties have allowed values, and
1062
 * some are dependent on other values (hence the need for @connection).  The
1063 1064 1065 1066 1067 1068
 * 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
1069
nm_setting_verify (NMSetting *setting, NMConnection *connection, GError **error)
1070
{
1071
	NMSettingVerifyResult result = _nm_setting_verify (setting, connection, error);
1072 1073 1074 1075 1076 1077 1078 1079

	if (result == NM_SETTING_VERIFY_NORMALIZABLE)
		g_clear_error (error);

	return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE;
}

NMSettingVerifyResult
1080
_nm_setting_verify (NMSetting *setting, NMConnection *connection, GError **error)
1081 1082
{
	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_VERIFY_ERROR);
1083
	g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), NM_SETTING_VERIFY_ERROR);
1084 1085 1086
	g_return_val_if_fail (!error || *error == NULL, NM_SETTING_VERIFY_ERROR);

	if (NM_SETTING_GET_CLASS (setting)->verify)
1087
		return NM_SETTING_GET_CLASS (setting)->verify (setting, connection, error);
1088 1089 1090 1091

	return NM_SETTING_VERIFY_SUCCESS;
}

1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
/**
 * 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;
}

1139 1140 1141 1142 1143 1144
static gboolean
compare_property (NMSetting *setting,
                  NMSetting *other,
                  const GParamSpec *prop_spec,
                  NMSettingCompareFlags flags)
{
1145
	const NMSettInfoProperty *property;
1146
	GVariant *value1, *value2;
1147
	int cmp;
1148 1149 1150 1151 1152 1153

	/* Handle compare flags */
	if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
		NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
		NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;

1154 1155 1156 1157 1158 1159
		g_return_val_if_fail (!NM_IS_SETTING_VPN (setting), FALSE);

		if (!nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL))
			g_return_val_if_reached (FALSE);
		if (!nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL))
			g_return_val_if_reached (FALSE);
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176

		/* If the secret flags aren't the same the settings aren't the same */
		if (a_secret_flags != b_secret_flags)
			return FALSE;

		/* Check for various secret flags that might cause us to ignore comparing
		 * this property.
		 */
		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
			return TRUE;

		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
			return TRUE;
	}

1177
	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), prop_spec->name);
1178 1179
	g_return_val_if_fail (property != NULL, FALSE);

1180 1181
	value1 = get_property_for_dbus (setting, property, TRUE);
	value2 = get_property_for_dbus (other, property, TRUE);
1182

1183
	cmp = nm_property_compare (value1, value2);
1184

1185 1186 1187 1188
	if (value1)
		g_variant_unref (value1);
	if (value2)
		g_variant_unref (value2);
1189

1190
	return cmp == 0;
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
}

/**
 * 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)
{
1210
	const NMSettInfoSetting *sett_info;
1211 1212
	GParamSpec **property_specs;
	guint n_property_specs;
1213
	int same = TRUE;
1214 1215 1216 1217 1218 1219 1220 1221 1222
	guint i;

	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
	g_return_val_if_fail (NM_IS_SETTING (b), FALSE);

	/* First check that both have the same type */
	if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
		return FALSE;

1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));

	if (sett_info->detail.gendata_info) {
		GenData *a_gendata = _gendata_hash (a, FALSE);
		GenData *b_gendata = _gendata_hash (b, FALSE);

		return nm_utils_hash_table_equal (a_gendata ? a_gendata->hash : NULL,
		                                  b_gendata ? b_gendata->hash : NULL,
		                                  TRUE,
		                                  g_variant_equal);
	}

1235 1236 1237 1238 1239 1240
	/* And now all properties */
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
	for (i = 0; i < n_property_specs && same; i++) {
		GParamSpec *prop_spec = property_specs[i];

		/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
1241 1242
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_FUZZY)
		    && !NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))
1243 1244
			continue;

1245 1246
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
		    && !NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_INFERRABLE))
1247 1248
			continue;

1249 1250 1251 1252
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)
		    && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
			continue;

1253 1254
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
		    && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET))
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
			continue;

		same = NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
	}
	g_free (property_specs);

	return same;
}

static inline gboolean
should_compare_prop (NMSetting *setting,
                     const char *prop_name,
                     NMSettingCompareFlags comp_flags,
                     GParamFlags prop_flags)
{
	/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY)
	    && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
		return FALSE;

	if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
		return FALSE;

1278 1279 1280
	if ((comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY) && !(prop_flags & NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
		return FALSE;

1281 1282 1283 1284 1285 1286
	if (prop_flags & NM_SETTING_PARAM_SECRET) {
		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;

		if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
			return FALSE;

1287 1288 1289 1290
		if (   NM_IS_SETTING_VPN (setting)
		    && g_strcmp0 (prop_name, NM_SETTING_VPN_SECRETS) == 0) {
			/* FIXME: NMSettingVPN:NM_SETTING_VPN_SECRETS has NM_SETTING_PARAM_SECRET.
			 * nm_setting_get_secret_flags() quite possibly fails, but it might succeed if the
luz.paz's avatar
luz.paz committed
1291
			 * setting accidentally uses a key "secrets". */
1292 1293 1294 1295 1296
			return TRUE;
		}

		if (!nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL))
			g_return_val_if_reached (FALSE);
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310

		if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
		    && (secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
			return FALSE;

		if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
		    && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
			return FALSE;
	}

	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
	    && NM_IS_SETTING_CONNECTION (setting)
	    && !strcmp (prop_name, NM_SETTING_CONNECTION_ID))
		return FALSE;
1311 1312 1313 1314 1315

	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)
	    && NM_IS_SETTING_CONNECTION (setting)
	    && !strcmp (prop_name, NM_SETTING_CONNECTION_TIMESTAMP))
		return FALSE;
1316 1317 1318 1319

	return TRUE;
}

1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
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));
}

1335 1336 1337 1338 1339
/**
 * 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
1340
 * @invert_results: this parameter is used internally by libnm and should
1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
 * 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)
{
1363
	const NMSettInfoSetting *sett_info;
1364 1365 1366
	guint i;
	NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
	NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
1367 1368
	NMSettingDiffResult a_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
	NMSettingDiffResult b_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
1369
	gboolean results_created = FALSE;
1370
	gboolean compared_any = FALSE;
1371
	gboolean diff_found = FALSE;
1372 1373 1374 1375 1376 1377 1378 1379

	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);
	}

1380 1381 1382 1383 1384 1385
	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;
	}

1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
	/* 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;
1398 1399
		a_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
		b_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
1400 1401 1402
	}

	if (*results == NULL) {
1403
		*results = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
1404 1405 1406
		results_created = TRUE;
	}

1407
	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
1408

1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429
	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)) {
1430
				val2 = g_hash_table_lookup (b_gendata->hash, key);
1431 1432 1433 1434 1435 1436 1437 1438 1439
				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)) {
1440
				val2 = g_hash_table_lookup (a_gendata->hash, key);
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451
				compared_any = TRUE;
				if (   !val2
				    || !g_variant_equal (val, val2)) {
					diff_found = TRUE;
					_setting_diff_add_result (*results, key, b_result);
				}
			}
		}
	} else {
		gs_free GParamSpec **property_specs = NULL;
		guint n_property_specs;
1452

1453
		property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);