diff options
Diffstat (limited to 'libnm-util/nm-setting.c')
| -rw-r--r-- | libnm-util/nm-setting.c | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c new file mode 100644 index 00000000..06994c73 --- /dev/null +++ b/libnm-util/nm-setting.c @@ -0,0 +1,752 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * Tambet Ingo <tambet@gmail.com> + * + * 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. + * + * (C) Copyright 2007 - 2008 Red Hat, Inc. + * (C) Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> + +#include "nm-setting.h" +#include "nm-setting-connection.h" +#include "nm-utils.h" + +/** + * SECTION:nm-setting + * @short_description: Describes related configuration information + * @include: nm-setting.h + * + * 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 WiFi). 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. + */ + +/** + * nm_setting_error_quark: + * + * Registers an error quark for #NMSetting if necessary. + * + * Returns: the error quark used for NMSetting errors. + **/ +GQuark +nm_setting_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-error-quark"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +nm_setting_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (NM_SETTING_ERROR_UNKNOWN, "UnknownError"), + ENUM_ENTRY (NM_SETTING_ERROR_PROPERTY_NOT_FOUND, "PropertyNotFound"), + ENUM_ENTRY (NM_SETTING_ERROR_PROPERTY_NOT_SECRET, "PropertyNotSecret"), + ENUM_ENTRY (NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, "PropertyTypeMismatch"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMSettingError", values); + } + return etype; +} + +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)) + +typedef struct { + char *name; +} NMSettingPrivate; + +enum { + PROP_0, + PROP_NAME, + + PROP_LAST +}; + +static void +destroy_gvalue (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +/** + * nm_setting_to_hash: + * @setting: the #NMSetting + * + * Converts the #NMSetting into a #GHashTable mapping each setting property + * name to a GValue describing that property, suitable for marshalling over + * D-Bus or serializing. The mapping is string:GValue. + * + * Returns: a new #GHashTable describing the setting's properties + **/ +GHashTable * +nm_setting_to_hash (NMSetting *setting) +{ + GHashTable *hash; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + if (!property_specs) { + g_warning ("%s: couldn't find property specs for object of type '%s'", + __func__, g_type_name (G_OBJECT_TYPE (setting))); + return NULL; + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + destroy_gvalue); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + + if (prop_spec->flags & NM_SETTING_PARAM_SERIALIZE) { + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, value); + + /* Don't serialize values with default values */ + if (!g_param_value_defaults (prop_spec, value)) + g_hash_table_insert (hash, g_strdup (prop_spec->name), value); + else + destroy_gvalue (value); + } + } + + g_free (property_specs); + + return hash; +} + +typedef struct { + GObjectClass *class; + guint n_params; + GParameter *params; +} NMSettingFromHashInfo; + +static void +one_property_cb (gpointer key, gpointer val, gpointer user_data) +{ + const char *prop_name = (char *) key; + GValue *src_value = (GValue *) val; + NMSettingFromHashInfo *info = (NMSettingFromHashInfo *) user_data; + GValue *dst_value = &info->params[info->n_params].value; + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (info->class, prop_name); + if (!param_spec || !(param_spec->flags & NM_SETTING_PARAM_SERIALIZE)) { + /* Oh, we're so nice and only warn, maybe it should be a fatal error? */ + nm_warning ("Ignoring invalid property '%s'", prop_name); + return; + } + + g_value_init (dst_value, G_VALUE_TYPE (src_value)); + if (g_value_transform (src_value, dst_value)) { + info->params[info->n_params].name = prop_name; + info->n_params++; + } else { + nm_warning ("Ignoring property '%s' with invalid type (%s)", + prop_name, G_VALUE_TYPE_NAME (src_value)); + g_value_unset (dst_value); + } +} + +/** + * nm_setting_new_from_hash: + * @setting_type: the #NMSetting type which the hash contains properties for + * @hash: the #GHashTable containing a string:GValue mapping of properties + * that apply to the setting + * + * Creates a new #NMSetting object and populates that object with the properties + * contained in the hash table, using each hash key as the property to set, + * and each hash value as the value to set that property to. Setting properties + * are strongly typed, thus the GValue type of the hash 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 on failure + **/ +NMSetting * +nm_setting_new_from_hash (GType setting_type, + GHashTable *hash) +{ + NMSetting *setting; + NMSettingFromHashInfo info; + int i; + + g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL); + g_return_val_if_fail (hash != NULL, NULL); + + info.class = g_type_class_ref (setting_type); + info.n_params = 0; + info.params = g_new0 (GParameter, g_hash_table_size (hash)); + + g_hash_table_foreach (hash, one_property_cb, &info); + + setting = (NMSetting *) g_object_newv (setting_type, info.n_params, info.params); + + for (i = 0; i < info.n_params; i++) { + GValue *v = &info.params[i].value; + g_value_unset (v); + } + + g_free (info.params); + g_type_class_unref (info.class); + + return setting; +} + +static void +duplicate_setting (NMSetting *setting, + const char *name, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + if (flags & 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: a new #NMSetting containing the same properties and values as the + * source #NMSetting + **/ +NMSetting * +nm_setting_duplicate (NMSetting *setting) +{ + GObject *dup; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + dup = g_object_new (G_OBJECT_TYPE (setting), NULL); + + 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) +{ + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + return NM_SETTING_GET_PRIVATE (setting)->name; +} + +/** + * nm_setting_verify: + * @setting: the #NMSetting to verify + * @all_settings: a #GSList of all settings in the connection from which @setting + * came + * @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 @all_settings). 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, GSList *all_settings, GError **error) +{ + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + if (NM_SETTING_GET_CLASS (setting)->verify) + return NM_SETTING_GET_CLASS (setting)->verify (setting, all_settings, error); + + return TRUE; +} + +/** + * 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) +{ + GParamSpec **property_specs; + guint n_property_specs; + gint different; + 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; + + /* And now all properties */ + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); + different = FALSE; + + for (i = 0; i < n_property_specs && !different; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value1 = { 0 }; + GValue value2 = { 0 }; + + /* Fuzzy compare ignores secrets and properties defined with the + * FUZZY_IGNORE flag + */ + if ( (flags & NM_SETTING_COMPARE_FLAG_FUZZY) + && (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))) + continue; + + if ((flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS) && (prop_spec->flags & NM_SETTING_PARAM_SECRET)) + continue; + + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID) + && !strcmp (nm_setting_get_name (a), NM_SETTING_CONNECTION_SETTING_NAME) + && !strcmp (prop_spec->name, NM_SETTING_CONNECTION_ID)) + continue; + + g_value_init (&value1, prop_spec->value_type); + g_object_get_property (G_OBJECT (a), prop_spec->name, &value1); + + g_value_init (&value2, prop_spec->value_type); + g_object_get_property (G_OBJECT (b), prop_spec->name, &value2); + + different = g_param_values_cmp (prop_spec, &value1, &value2); + + g_value_unset (&value1); + g_value_unset (&value2); + } + + g_free (property_specs); + + return different == 0 ? TRUE : FALSE; +} + +/** + * nm_setting_enumerate_values: + * @setting: the #NMSetting + * @func: 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) +{ + GParamSpec **property_specs; + guint n_property_specs; + int i; + + g_return_if_fail (NM_IS_SETTING (setting)); + g_return_if_fail (func != NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value = { 0 }; + + 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. + **/ +void +nm_setting_clear_secrets (NMSetting *setting) +{ + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + g_return_if_fail (NM_IS_SETTING (setting)); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value = { 0 }; + + if (prop_spec->flags & NM_SETTING_PARAM_SECRET) { + g_value_init (&value, prop_spec->value_type); + g_param_value_set_default (prop_spec, &value); + g_object_set_property (G_OBJECT (setting), prop_spec->name, &value); + g_value_unset (&value); + } + } + + g_free (property_specs); +} + +/** + * 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: a #GPtrArray containing the property names of secrets of the + * #NMSetting which may be required; the caller owns the array + * and must free the each array element with g_free(), as well as the array + * itself with g_ptr_array_free() + **/ +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; +} + +typedef struct { + NMSetting *setting; + GError **error; +} UpdateSecretsInfo; + +static gboolean +update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error) +{ + GParamSpec *prop_spec; + GValue transformed_value = { 0 }; + gboolean success = FALSE; + + prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key); + if (!prop_spec) { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_NOT_FOUND, + "%s", key); + return FALSE; + } + + if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET)) { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_NOT_SECRET, + "%s", key); + return FALSE; + } + + if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) { + g_object_set_property (G_OBJECT (setting), prop_spec->name, value); + success = TRUE; + } else if (g_value_transform (value, &transformed_value)) { + g_object_set_property (G_OBJECT (setting), prop_spec->name, &transformed_value); + g_value_unset (&transformed_value); + success = TRUE; + } else { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "%s", key); + } + return success; +} + +static void +update_one_cb (gpointer key, gpointer val, gpointer user_data) +{ + UpdateSecretsInfo *info = user_data; + const char *secret_key = (const char *) key; + GValue *secret_value = (GValue *) val; + + if (*(info->error) == NULL) + NM_SETTING_GET_CLASS (info->setting)->update_one_secret (info->setting, secret_key, secret_value, info->error); +} + +/** + * nm_setting_update_secrets: + * @setting: the #NMSetting + * @secrets: a #GHashTable mapping string:#GValue of setting property names and + * secrets + * @error: location to store error, or %NULL + * + * Update the setting's secrets, given a hash table of secrets intended for that + * setting (deserialized from D-Bus for example). + * + * Returns: %TRUE if the secrets were successfully updated and the connection + * is valid, %FALSE on failure or if the setting was never added to the connection + **/ +gboolean +nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error) +{ + UpdateSecretsInfo *info; + gboolean success; + + g_return_val_if_fail (setting != NULL, FALSE); + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (secrets != NULL, FALSE); + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + info = g_malloc0 (sizeof (UpdateSecretsInfo)); + info->setting = setting; + info->error = error; + g_hash_table_foreach (secrets, update_one_cb, info); + success = *(info->error) ? FALSE : TRUE; + g_free (info); + + return success; +} + +/** + * nm_setting_to_string: + * @setting: the #NMSetting + * + * Convert the setting 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 (including secrets!), which the caller should + * free with g_free() + **/ +char * +nm_setting_to_string (NMSetting *setting) +{ + GString *string; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + if (!property_specs) + return NULL; + + string = g_string_new (nm_setting_get_name (setting)); + g_string_append_c (string, '\n'); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value = { 0 }; + char *value_str; + gboolean is_serializable; + gboolean is_default; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); + + value_str = g_strdup_value_contents (&value); + g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str); + g_free (value_str); + + is_serializable = prop_spec->flags & NM_SETTING_PARAM_SERIALIZE; + is_default = g_param_value_defaults (prop_spec, &value); + + if (is_serializable || is_default) { + g_string_append (string, " ("); + + if (is_serializable) + g_string_append_c (string, 's'); + if (is_default) + g_string_append_c (string, 'd'); + + g_string_append_c (string, ')'); + } + + g_string_append_c (string, '\n'); + } + + g_free (property_specs); + g_string_append_c (string, '\n'); + + return g_string_free (string, FALSE); +} + +/*****************************************************************************/ + +static void +nm_setting_init (NMSetting *setting) +{ +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMSettingPrivate *priv; + + object = G_OBJECT_CLASS (nm_setting_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_SETTING_GET_PRIVATE (object); + if (!priv->name) { + nm_warning ("Setting name is not set."); + g_object_unref (object); + object = NULL; + } + + return object; +} + +static void +finalize (GObject *object) +{ + NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object); + + g_free (priv->name); + + G_OBJECT_CLASS (nm_setting_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + g_free (priv->name); + priv->name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSetting *setting = NM_SETTING (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, nm_setting_get_name (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_class_init (NMSettingClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + setting_class->update_one_secret = update_one_secret; + + /* Properties */ + + /** + * 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 'wireless' or 'wired'. + **/ + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (NM_SETTING_NAME, + "Name", + "The setting's name; these names are defined by the " + "specification and cannot be changed after the object " + "has been created. Each setting class has a name, and " + "all objects of that class share the same name.", + NULL, + G_PARAM_READWRITE)); +} + |