Commit acf86f68 authored by Dan Winship's avatar Dan Winship

libnm-core: change connection hash tables to variants in API

In preparation for porting to GDBus, make nm_connection_to_dbus(),
etc, represent connections as GVariants of type 'a{sa{sv}}' rather
than as GHashTables-of-GHashTables-of-GValues.

This means we're constantly converting back and forth internally, but
this is just a stepping stone on the way to the full GDBus port, and
all of that code will go away again later.
parent 47505595
......@@ -406,7 +406,7 @@ nm_dispatcher_utils_construct_envp (const char *action,
}
/* UUID and ID */
con_setting = g_variant_lookup_value (connection_dict, NM_SETTING_CONNECTION_SETTING_NAME, G_VARIANT_TYPE ("a{sv}"));
con_setting = g_variant_lookup_value (connection_dict, NM_SETTING_CONNECTION_SETTING_NAME, NM_VARIANT_TYPE_SETTING);
if (!con_setting) {
g_warning ("Failed to read connection setting");
return NULL;
......
......@@ -30,25 +30,9 @@
#include "nm-dispatcher-utils.h"
#include "nm-dispatcher-api.h"
#include "nm-utils.h"
#include "nm-dbus-glib-types.h"
/*******************************************/
static GVariant *
connection_hash_to_dict (GHashTable *hash)
{
GValue val = { 0, };
GVariant *dict;
g_value_init (&val, DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT);
g_value_set_boxed (&val, hash);
dict = dbus_g_value_build_g_variant (&val);
g_value_unset (&val);
g_variant_ref_sink (dict);
return dict;
}
static gboolean
parse_main (GKeyFile *kf,
GVariant **out_con_dict,
......@@ -62,7 +46,6 @@ parse_main (GKeyFile *kf,
NMConnection *connection;
NMSettingConnection *s_con;
GVariantBuilder props;
GHashTable *con_hash;
*out_expected_iface = g_key_file_get_string (kf, "main", "expected-iface", error);
if (*out_expected_iface == NULL)
......@@ -93,10 +76,8 @@ parse_main (GKeyFile *kf,
g_free (id);
nm_connection_add_setting (connection, NM_SETTING (s_con));
con_hash = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
*out_con_dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
g_object_unref (connection);
*out_con_dict = connection_hash_to_dict (con_hash);
g_hash_table_unref (con_hash);
g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&props, "{sv}",
......
......@@ -5343,19 +5343,19 @@ gen_cmd_print0 (const char *text, int state)
char *ret = NULL;
if (!state) {
GHashTable *settings;
GHashTableIter iter;
GVariant *settings;
GVariantIter iter;
const char *setting_name;
int i = 0;
settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
words = g_new (char *, g_hash_table_size (settings) + 2);
g_hash_table_iter_init (&iter, settings);
while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, NULL))
words = g_new (char *, g_variant_n_children (settings) + 2);
g_variant_iter_init (&iter, settings);
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
words [i++] = g_strdup (setting_name);
words[i++] = g_strdup ("all");
words[i] = NULL;
g_hash_table_unref (settings);
g_variant_unref (settings);
}
if (words) {
......
......@@ -162,31 +162,21 @@ save_connection_and_exit (NmtNewtButton *button,
static void
got_secrets (NMRemoteConnection *connection,
GHashTable *secrets,
GVariant *secrets,
GError *error,
gpointer op)
{
GHashTable *copy = NULL, *setting;
GHashTableIter iter;
const char *name;
if (secrets) {
/* 'secrets' is owned by the caller so we must copy it */
copy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
g_hash_table_iter_init (&iter, secrets);
while (g_hash_table_iter_next (&iter, (gpointer) &name, (gpointer) &setting))
g_hash_table_insert (copy, g_strdup (name), nm_utils_gvalue_hash_dup (setting));
}
nmt_sync_op_complete_pointer (op, copy, error);
if (secrets)
g_variant_ref (secrets);
nmt_sync_op_complete_pointer (op, secrets, error);
}
static NMConnection *
build_edit_connection (NMConnection *orig_connection)
{
NMConnection *edit_connection;
GHashTable *settings, *secrets;
GHashTableIter iter;
GVariant *settings, *secrets;
GVariantIter iter;
const char *setting_name;
NmtSyncOp op;
......@@ -196,8 +186,8 @@ build_edit_connection (NMConnection *orig_connection)
return edit_connection;
settings = nm_connection_to_dbus (orig_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
g_hash_table_iter_init (&iter, settings);
while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, NULL)) {
g_variant_iter_init (&iter, settings);
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL)) {
nmt_sync_op_init (&op);
nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (orig_connection),
setting_name, got_secrets, &op);
......@@ -205,10 +195,10 @@ build_edit_connection (NMConnection *orig_connection)
secrets = nmt_sync_op_wait_pointer (&op, NULL);
if (secrets) {
(void) nm_connection_update_secrets (edit_connection, setting_name, secrets, NULL);
g_hash_table_unref (secrets);
g_variant_unref (secrets);
}
}
g_hash_table_unref (settings);
g_variant_unref (settings);
return edit_connection;
}
......
......@@ -482,14 +482,6 @@ nmt_secret_agent_get_secrets (NMSecretAgent *agent,
request_secrets_from_ui (request);
}
static void
gvalue_destroy_notify (gpointer data)
{
GValue *value = data;
g_value_unset (value);
g_slice_free (GValue, value);
}
/**
* nmt_secret_agent_response:
* @self: the #NmtSecretAgent
......@@ -511,8 +503,7 @@ nmt_secret_agent_response (NmtSecretAgent *self,
{
NmtSecretAgentPrivate *priv;
NmtSecretAgentRequest *request;
GHashTable *hash = NULL, *setting_hash;
GValue *value;
GVariant *dict = NULL;
GError *error = NULL;
int i;
......@@ -523,32 +514,41 @@ nmt_secret_agent_response (NmtSecretAgent *self,
g_return_if_fail (request != NULL);
if (secrets) {
hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_unref);
GVariantBuilder conn_builder, *setting_builder;
GHashTable *settings;
GHashTableIter iter;
const char *name;
settings = g_hash_table_new (g_str_hash, g_str_equal);
for (i = 0; i < secrets->len; i++) {
NmtSecretAgentSecretReal *secret = secrets->pdata[i];
setting_hash = g_hash_table_lookup (hash, nm_setting_get_name (secret->setting));
if (!setting_hash) {
setting_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, gvalue_destroy_notify);
g_hash_table_insert (hash, (char *)nm_setting_get_name (secret->setting),
setting_hash);
setting_builder = g_hash_table_lookup (settings, nm_setting_get_name (secret->setting));
if (!setting_builder) {
setting_builder = g_variant_builder_new (NM_VARIANT_TYPE_SETTING);
g_hash_table_insert (settings, (char *) nm_setting_get_name (secret->setting),
setting_builder);
}
value = g_slice_new0 (GValue);
g_value_init (value, G_TYPE_STRING);
g_value_set_string (value, secret->base.value);
g_hash_table_insert (setting_hash, g_strdup (secret->property), value);
g_variant_builder_add (setting_builder, "{sv}",
secret->property,
g_variant_new_string (secret->base.value));
}
g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION);
g_hash_table_iter_init (&iter, settings);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder))
g_variant_builder_add (&conn_builder, "{sa{sv}}", name, setting_builder);
dict = g_variant_builder_end (&conn_builder);
g_hash_table_destroy (settings);
} else {
error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED,
"User cancelled");
}
request->callback (NM_SECRET_AGENT (self), request->connection, hash, error, request->callback_data);
request->callback (NM_SECRET_AGENT (self), request->connection, dict, error, request->callback_data);
g_clear_pointer (&hash, g_hash_table_unref);
g_clear_pointer (&dict, g_variant_unref);
g_clear_error (&error);
g_hash_table_remove (priv->requests, request_id);
}
......
......@@ -62,8 +62,7 @@ GTKDOC_CFLAGS = \
-I$(top_srcdir)/libnm \
-I$(top_builddir)/libnm \
-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
$(GLIB_CFLAGS) \
$(DBUS_CFLAGS)
$(GLIB_CFLAGS)
GTKDOC_LIBS = \
$(top_builddir)/libnm/libnm.la \
......
......@@ -81,6 +81,5 @@ libnm_core_sources = \
$(core)/nm-setting-wireless.c \
$(core)/nm-setting.c \
$(core)/nm-simple-connection.c \
$(core)/nm-utils.c \
$(core)/nm-value-transforms.c
$(core)/nm-utils.c
......@@ -22,11 +22,9 @@
#include <glib-object.h>
#include <glib/gi18n.h>
#include <dbus/dbus-glib.h>
#include <string.h>
#include "nm-connection.h"
#include "nm-utils-internal.h"
#include "nm-dbus-glib-types.h"
#include "nm-utils.h"
#include "nm-setting-private.h"
#include "nm-setting-8021x.h"
......@@ -243,35 +241,40 @@ nm_connection_get_setting_by_name (NMConnection *connection, const char *name)
}
static gboolean
validate_permissions_type (GHashTable *hash, GError **error)
validate_permissions_type (GVariant *variant, GError **error)
{
GHashTable *s_con;
GValue *permissions;
GVariant *s_con;
GVariant *permissions;
gboolean valid = TRUE;
/* Ensure the connection::permissions item (if present) is the correct
* type, otherwise the g_object_set() will throw a warning and ignore the
* error, leaving us with no permissions.
*/
s_con = g_hash_table_lookup (hash, NM_SETTING_CONNECTION_SETTING_NAME);
if (s_con) {
permissions = g_hash_table_lookup (s_con, NM_SETTING_CONNECTION_PERMISSIONS);
s_con = g_variant_lookup_value (variant, NM_SETTING_CONNECTION_SETTING_NAME, NM_VARIANT_TYPE_SETTING);
if (!s_con)
return TRUE;
permissions = g_variant_lookup_value (s_con, NM_SETTING_CONNECTION_PERMISSIONS, NULL);
if (permissions) {
if (!G_VALUE_HOLDS (permissions, G_TYPE_STRV)) {
if (!g_variant_is_of_type (permissions, G_VARIANT_TYPE_STRING_ARRAY)) {
g_set_error_literal (error,
NM_SETTING_ERROR,
NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
"Wrong permissions property type; should be an array of strings.");
return FALSE;
}
"Wrong permissions property type; should be a list of strings.");
valid = FALSE;
}
g_variant_unref (permissions);
}
return TRUE;
g_variant_unref (s_con);
return valid;
}
/**
* nm_connection_replace_settings:
* @connection: a #NMConnection
* @new_settings: (element-type utf8 GLib.HashTable): a #GHashTable of settings
* @new_settings: a #GVariant of type %NM_VARIANT_TYPE_CONNECTION, with the new settings
* @error: location to store error, or %NULL
*
* Replaces @connection's settings with @new_settings (which must be
......@@ -283,18 +286,18 @@ validate_permissions_type (GHashTable *hash, GError **error)
**/
gboolean
nm_connection_replace_settings (NMConnection *connection,
GHashTable *new_settings,
GVariant *new_settings,
GError **error)
{
NMConnectionPrivate *priv;
GHashTableIter iter;
GVariantIter iter;
const char *setting_name;
GHashTable *setting_hash;
GVariant *setting_dict;
GSList *settings = NULL, *s;
gboolean changed;
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
g_return_val_if_fail (new_settings != NULL, FALSE);
g_return_val_if_fail (g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
priv = NM_CONNECTION_GET_PRIVATE (connection);
......@@ -302,8 +305,8 @@ nm_connection_replace_settings (NMConnection *connection,
if (!validate_permissions_type (new_settings, error))
return FALSE;
g_hash_table_iter_init (&iter, new_settings);
while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) {
g_variant_iter_init (&iter, new_settings);
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, &setting_dict)) {
NMSetting *setting;
GType type;
......@@ -313,15 +316,19 @@ nm_connection_replace_settings (NMConnection *connection,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_SETTING,
"unknown setting name '%s'", setting_name);
g_variant_unref (setting_dict);
g_slist_free_full (settings, g_object_unref);
return FALSE;
}
setting = _nm_setting_new_from_dbus (type, setting_hash, new_settings, error);
setting = _nm_setting_new_from_dbus (type, setting_dict, new_settings, error);
g_variant_unref (setting_dict);
if (!setting) {
g_slist_free_full (settings, g_object_unref);
return FALSE;
}
settings = g_slist_prepend (settings, setting);
}
......@@ -928,16 +935,16 @@ nm_connection_normalize (NMConnection *connection,
* nm_connection_update_secrets:
* @connection: the #NMConnection
* @setting_name: the setting object name to which the secrets apply
* @secrets: (element-type utf8 GObject.Value): a #GHashTable mapping
* string:#GValue of setting property names and secrets of the given @setting_name
* @secrets: a #GVariant of secrets, of type %NM_VARIANT_TYPE_CONNECTION
* or %NM_VARIANT_TYPE_SETTING
* @error: location to store error, or %NULL
*
* Update the specified setting's secrets, given a hash table of secrets
* Update the specified setting's secrets, given a dictionary of secrets
* intended for that setting (deserialized from D-Bus for example). Will also
* extract the given setting's secrets hash if given a hash of hashes, as would
* be returned from nm_connection_to_dbus(). If @setting_name is %NULL, expects
* a fully serialized #NMConnection as returned by nm_connection_to_dbus() and
* will update all secrets from all settings contained in @secrets.
* extract the given setting's secrets hash if given a connection dictionary.
* If @setting_name is %NULL, expects a fully serialized #NMConnection as
* returned by nm_connection_to_dbus() and will update all secrets from all
* settings contained in @secrets.
*
* Returns: %TRUE if the secrets were successfully updated, %FALSE if the update
* failed (tried to update secrets for a setting that doesn't exist, etc)
......@@ -945,38 +952,28 @@ nm_connection_normalize (NMConnection *connection,
gboolean
nm_connection_update_secrets (NMConnection *connection,
const char *setting_name,
GHashTable *secrets,
GVariant *secrets,
GError **error)
{
NMSetting *setting;
gboolean success = TRUE, updated = FALSE;
GHashTable *setting_hash = NULL;
GHashTableIter iter;
GVariant *setting_dict = NULL;
GVariantIter iter;
const char *key;
gboolean hashed_connection = FALSE;
gboolean full_connection;
int success_detail;
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
g_return_val_if_fail (secrets != NULL, FALSE);
g_return_val_if_fail ( g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING)
|| g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION), FALSE);
if (error)
g_return_val_if_fail (*error == NULL, FALSE);
/* Empty @secrets means success */
if (g_hash_table_size (secrets) == 0)
if (g_variant_n_children (secrets) == 0)
return TRUE;
/* For backwards compatibility, this function accepts either a hashed
* connection (GHashTable of GHashTables of GValues) or a single hashed
* setting (GHashTable of GValues).
*/
g_hash_table_iter_init (&iter, secrets);
while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) {
if (nm_setting_lookup_type (key) != G_TYPE_INVALID) {
/* @secrets looks like a hashed connection */
hashed_connection = TRUE;
break;
}
}
full_connection = g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION);
if (setting_name) {
/* Update just one setting's secrets */
......@@ -989,10 +986,10 @@ nm_connection_update_secrets (NMConnection *connection,
return FALSE;
}
if (hashed_connection) {
setting_hash = g_hash_table_lookup (secrets, setting_name);
if (!setting_hash) {
/* The hashed connection that didn't contain any secrets for
if (full_connection) {
setting_dict = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING);
if (!setting_dict) {
/* The connection dictionary didn't contain any secrets for
* @setting_name; just return success.
*/
return TRUE;
......@@ -1001,16 +998,18 @@ nm_connection_update_secrets (NMConnection *connection,
g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
success_detail = _nm_setting_update_secrets (setting,
setting_hash ? setting_hash : secrets,
setting_dict ? setting_dict : secrets,
error);
g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
g_clear_pointer (&setting_dict, g_variant_unref);
if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR)
return FALSE;
if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
updated = TRUE;
} else {
if (!hashed_connection) {
if (!full_connection) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
......@@ -1019,8 +1018,8 @@ nm_connection_update_secrets (NMConnection *connection,
}
/* check first, whether all the settings exist... */
g_hash_table_iter_init (&iter, secrets);
while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) {
g_variant_iter_init (&iter, secrets);
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &key, NULL)) {
setting = nm_connection_get_setting_by_name (connection, key);
if (!setting) {
g_set_error_literal (error,
......@@ -1031,16 +1030,18 @@ nm_connection_update_secrets (NMConnection *connection,
}
}
/* Update each setting with any secrets from the hashed connection */
g_hash_table_iter_init (&iter, secrets);
while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &setting_hash)) {
/* Update each setting with any secrets from the connection dictionary */
g_variant_iter_init (&iter, secrets);
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &key, &setting_dict)) {
/* Update the secrets for this setting */
setting = nm_connection_get_setting_by_name (connection, key);
g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
success_detail = _nm_setting_update_secrets (setting, setting_hash, error);
success_detail = _nm_setting_update_secrets (setting, setting_dict, error);
g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
g_variant_unref (setting_dict);
if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR) {
success = FALSE;
break;
......@@ -1184,46 +1185,43 @@ nm_connection_clear_secrets_with_flags (NMConnection *connection,
* @connection: the #NMConnection
* @flags: serialization flags, e.g. %NM_CONNECTION_SERIALIZE_ALL
*
* Converts the #NMConnection into a #GHashTable describing the connection,
* suitable for marshalling over D-Bus or otherwise serializing. The hash table
* mapping is string:#GHashTable with each element in the returned hash
* representing a #NMSetting object. The keys are setting object names, and the
* values are #GHashTables mapping string:GValue, each of which represents the
* properties of the #NMSetting object.
*
* Returns: (transfer full) (element-type utf8 GLib.HashTable): a new
* #GHashTable describing the connection, its settings, and each setting's
* properties. The caller owns the hash table and must unref the hash table
* with g_hash_table_unref() when it is no longer needed.
* Converts the #NMConnection into a #GVariant of type
* %NM_VARIANT_TYPE_CONNECTION describing the connection, suitable for
* marshalling over D-Bus or otherwise serializing.
*
* Returns: (transfer none): a new floating #GVariant describing the connection,
* its settings, and each setting's properties.
**/
GHashTable *
nm_connection_to_dbus (NMConnection *connection, NMConnectionSerializationFlags flags)
GVariant *
nm_connection_to_dbus (NMConnection *connection,
NMConnectionSerializationFlags flags)
{
NMConnectionPrivate *priv;
GVariantBuilder builder;
GHashTableIter iter;
gpointer key, data;
GHashTable *ret, *setting_hash;
GVariant *setting_dict, *ret;
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
ret = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) g_hash_table_destroy);
priv = NM_CONNECTION_GET_PRIVATE (connection);
g_variant_builder_init (&builder, NM_VARIANT_TYPE_CONNECTION);
/* Add each setting's hash to the main hash */
g_hash_table_iter_init (&iter, priv->settings);
while (g_hash_table_iter_next (&iter, &key, &data)) {
NMSetting *setting = NM_SETTING (data);
setting_hash = _nm_setting_to_dbus (setting, connection, flags);
if (setting_hash)
g_hash_table_insert (ret, g_strdup (nm_setting_get_name (setting)), setting_hash);
setting_dict = _nm_setting_to_dbus (setting, connection, flags);
if (setting_dict)
g_variant_builder_add (&builder, "{s@a{sv}}", nm_setting_get_name (setting), setting_dict);
}
ret = g_variant_builder_end (&builder);
/* Don't send empty hashes */
if (g_hash_table_size (ret) < 1) {
g_hash_table_destroy (ret);
if (g_variant_n_children (ret) == 0) {
g_variant_unref (ret);
ret = NULL;
}
......
......@@ -140,6 +140,25 @@ NMSetting *nm_connection_get_setting (NMConnection *connection,
NMSetting *nm_connection_get_setting_by_name (NMConnection *connection,
const char *name);
/**
* NM_VARIANT_TYPE_CONNECTION:
*
* #GVariantType for a dictionary mapping from setting names to
* %NM_VARIANT_TYPE_SETTING variants. This is used to represent an
* #NMConnection, and is the type taken by nm_simple_connection_new_from_dbus()
* and returned from nm_connection_to_dbus().
*/
#define NM_VARIANT_TYPE_CONNECTION (G_VARIANT_TYPE ("a{sa{sv}}"))
/**
* NM_VARIANT_TYPE_SETTING:
*
* #GVariantType for a dictionary mapping from property names to values. This is
* an alias for %G_VARIANT_TYPE_VARDICT, and is the type of each element of
* an %NM_VARIANT_TYPE_CONNECTION dictionary.
*/
#define NM_VARIANT_TYPE_SETTING G_VARIANT_TYPE_VARDICT
/**
* NMConnectionSerializationFlags: