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