summary refs log tree commit diff
path: root/src/settings/nm-settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings/nm-settings.c')
-rw-r--r--src/settings/nm-settings.c769
1 files changed, 449 insertions, 320 deletions
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 8e3fc582..2c6b7101 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -62,10 +62,12 @@
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 
+#include "nm-utils/nm-c-list.h"
+#include "nm-dbus-object.h"
 #include "devices/nm-device-ethernet.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
-#include "nm-bus-manager.h"
+#include "nm-dbus-manager.h"
 #include "nm-auth-utils.h"
 #include "nm-auth-subject.h"
 #include "nm-session-monitor.h"
@@ -77,8 +79,6 @@
 #include "nm-dispatcher.h"
 #include "nm-hostname-manager.h"
 
-#include "introspection/org.freedesktop.NetworkManager.Settings.h"
-
 /*****************************************************************************/
 
 #define EXPORT(sym) void * __export_##sym = &sym;
@@ -107,7 +107,6 @@ enum {
 	CONNECTION_UPDATED,
 	CONNECTION_REMOVED,
 	CONNECTION_FLAGS_CHANGED,
-	NEW_CONNECTION, /* exported, not used internally */
 	LAST_SIGNAL
 };
 
@@ -121,29 +120,33 @@ typedef struct {
 	GSList *auths;
 
 	GSList *plugins;
-	gboolean connections_loaded;
-	GHashTable *connections;
+
+	CList connections_lst_head;
+
 	NMSettingsConnection **connections_cached_list;
 	GSList *unmanaged_specs;
 	GSList *unrecognized_specs;
 
-	gboolean started;
-	gboolean startup_complete;
-
 	NMHostnameManager *hostname_manager;
 
+	guint connections_len;
+
+	bool started:1;
+	bool startup_complete:1;
+	bool connections_loaded:1;
+
 } NMSettingsPrivate;
 
 struct _NMSettings {
-	NMExportedObject parent;
+	NMDBusObject parent;
 	NMSettingsPrivate _priv;
 };
 
 struct _NMSettingsClass {
-	NMExportedObjectClass parent;
+	NMDBusObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT);
+G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT);
 
 #define NM_SETTINGS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettings, NM_IS_SETTINGS)
 
@@ -154,6 +157,10 @@ G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT);
 
 /*****************************************************************************/
 
+static const NMDBusInterfaceInfoExtended interface_info_settings;
+static const GDBusSignalInfo signal_info_new_connection;
+static const GDBusSignalInfo signal_info_connection_removed;
+
 static void claim_connection (NMSettings *self,
                               NMSettingsConnection *connection);
 
@@ -164,27 +171,29 @@ static void connection_ready_changed (NMSettingsConnection *conn,
                                       GParamSpec *pspec,
                                       gpointer user_data);
 
+static void default_wired_clear_tag (NMSettings *self,
+                                     NMDevice *device,
+                                     NMSettingsConnection *connection,
+                                     gboolean add_to_no_auto_default);
+
 /*****************************************************************************/
 
 static void
 check_startup_complete (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GHashTableIter iter;
 	NMSettingsConnection *conn;
 
 	if (priv->startup_complete)
 		return;
 
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn)) {
+	c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst) {
 		if (!nm_settings_connection_get_ready (conn))
 			return;
 	}
 
 	/* the connection_ready_changed signal handler is no longer needed. */
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn))
+	c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst)
 		g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_ready_changed), self);
 
 	priv->startup_complete = TRUE;
@@ -247,43 +256,25 @@ load_connections (NMSettings *self)
 	unrecognized_specs_changed (NULL, self);
 }
 
-void
-nm_settings_for_each_connection (NMSettings *self,
-                                 NMSettingsForEachFunc for_each_func,
-                                 gpointer user_data)
-{
-	NMSettingsPrivate *priv;
-	GHashTableIter iter;
-	gpointer data;
-
-	g_return_if_fail (NM_IS_SETTINGS (self));
-	g_return_if_fail (for_each_func != NULL);
-
-	priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		for_each_func (self, NM_SETTINGS_CONNECTION (data), user_data);
-}
-
 static void
-impl_settings_list_connections (NMSettings *self,
-                                GDBusMethodInvocation *context)
+impl_settings_list_connections (NMDBusObject *obj,
+                                const NMDBusInterfaceInfoExtended *interface_info,
+                                const NMDBusMethodInfoExtended *method_info,
+                                GDBusConnection *dbus_connection,
+                                const char *sender,
+                                GDBusMethodInvocation *invocation,
+                                GVariant *parameters)
 {
+	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GPtrArray *connections;
-	GHashTableIter iter;
-	gpointer key;
-
-	connections = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, &key, NULL))
-		g_ptr_array_add (connections, key);
-	g_ptr_array_add (connections, NULL);
-
-	g_dbus_method_invocation_return_value (context,
-	                                       g_variant_new ("(^ao)", connections->pdata));
-	g_ptr_array_unref (connections);
+	gs_free const char **strv = NULL;
+
+	strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
+	                                          priv->connections_len,
+	                                          G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
+	                                          TRUE);
+	g_dbus_method_invocation_return_value (invocation,
+	                                       g_variant_new ("(^ao)", strv));
 }
 
 NMSettingsConnection *
@@ -291,16 +282,14 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
 {
 	NMSettingsPrivate *priv;
 	NMSettingsConnection *candidate;
-	GHashTableIter iter;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
-		if (g_strcmp0 (uuid, nm_settings_connection_get_uuid (candidate)) == 0)
+	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
+		if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate)))
 			return candidate;
 	}
 
@@ -308,14 +297,21 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
 }
 
 static void
-impl_settings_get_connection_by_uuid (NMSettings *self,
-                                      GDBusMethodInvocation *context,
-                                      const char *uuid)
+impl_settings_get_connection_by_uuid (NMDBusObject *obj,
+                                      const NMDBusInterfaceInfoExtended *interface_info,
+                                      const NMDBusMethodInfoExtended *method_info,
+                                      GDBusConnection *dbus_connection,
+                                      const char *sender,
+                                      GDBusMethodInvocation *invocation,
+                                      GVariant *parameters)
 {
+	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsConnection *connection = NULL;
-	NMAuthSubject *subject = NULL;
+	gs_unref_object NMAuthSubject *subject = NULL;
 	GError *error = NULL;
-	char *error_desc = NULL;
+	const char *uuid;
+
+	g_variant_get (parameters, "(&s)", &uuid);
 
 	connection = nm_settings_get_connection_by_uuid (self, uuid);
 	if (!connection) {
@@ -325,7 +321,7 @@ impl_settings_get_connection_by_uuid (NMSettings *self,
 		goto error;
 	}
 
-	subject = nm_auth_subject_new_unix_process_from_context (context);
+	subject = nm_auth_subject_new_unix_process_from_context (invocation);
 	if (!subject) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -333,26 +329,40 @@ impl_settings_get_connection_by_uuid (NMSettings *self,
 		goto error;
 	}
 
-	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
-	                                subject,
-	                                &error_desc)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		g_free (error_desc);
+	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
+	                                          subject,
+	                                          NM_SETTINGS_ERROR,
+	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
+	                                          &error))
 		goto error;
-	}
 
-	g_clear_object (&subject);
-	g_dbus_method_invocation_return_value (
-		context,
-		g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
+	g_dbus_method_invocation_return_value (invocation,
+	                                       g_variant_new ("(o)",
+	                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))));
 	return;
 
 error:
-	g_assert (error);
-	g_dbus_method_invocation_take_error (context, error);
-	g_clear_object (&subject);
+	g_dbus_method_invocation_take_error (invocation, error);
+}
+
+static void
+_clear_connections_cached_list (NMSettingsPrivate *priv)
+{
+	if (!priv->connections_cached_list)
+		return;
+
+	nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list));
+
+#if NM_MORE_ASSERTS
+	/* set the pointer to a bogus value. This makes it more apparent
+	 * if somebody has a reference to the cached list and still uses
+	 * it. That is a bug, this code just tries to make it blow up
+	 * more eagerly. */
+	memset (priv->connections_cached_list,
+	        0xdeaddead,
+	        sizeof (NMSettingsConnection *) * (priv->connections_len + 1));
+#endif
+	nm_clear_g_free (&priv->connections_cached_list);
 }
 
 /**
@@ -370,37 +380,33 @@ error:
 NMSettingsConnection *const*
 nm_settings_get_connections (NMSettings *self, guint *out_len)
 {
-	GHashTableIter iter;
 	NMSettingsPrivate *priv;
-	guint l, i;
 	NMSettingsConnection **v;
 	NMSettingsConnection *con;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	if (G_LIKELY (priv->connections_cached_list)) {
-		NM_SET_OUT (out_len, g_hash_table_size (priv->connections));
-		return priv->connections_cached_list;
-	}
+	nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head));
 
-	l = g_hash_table_size (priv->connections);
+	if (G_UNLIKELY (!priv->connections_cached_list)) {
+		v = g_new (NMSettingsConnection *, priv->connections_len + 1);
 
-	v = g_new (NMSettingsConnection *, (gsize) l + 1);
+		i = 0;
+		c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) {
+			nm_assert (i < priv->connections_len);
+			v[i++] = con;
+		}
+		nm_assert (i == priv->connections_len);
+		v[i] = NULL;
 
-	i = 0;
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) {
-		nm_assert (i < l);
-		v[i++] = con;
+		priv->connections_cached_list = v;
 	}
-	nm_assert (i == l);
-	v[i] = NULL;
 
-	NM_SET_OUT (out_len, l);
-	priv->connections_cached_list = v;
-	return v;
+	NM_SET_OUT (out_len, priv->connections_len);
+	return priv->connections_cached_list;
 }
 
 /**
@@ -467,28 +473,41 @@ NMSettingsConnection *
 nm_settings_get_connection_by_path (NMSettings *self, const char *path)
 {
 	NMSettingsPrivate *priv;
+	NMSettingsConnection *connection;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
-	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (path, NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path);
+	connection = (NMSettingsConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                                                     path);
+	if (   !connection
+	    || !NM_IS_SETTINGS_CONNECTION (connection))
+		return NULL;
+
+	nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
+	return connection;
 }
 
 gboolean
 nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GHashTableIter iter;
-	gpointer data;
+	NMSettingsConnection *candidate = NULL;
+	const char *path;
 
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		if (data == connection)
-			return TRUE;
+	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
 
-	return FALSE;
+	path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection));
+	if (path)
+		candidate = nm_settings_get_connection_by_path (self, path);
+
+	nm_assert (!candidate || candidate == connection);
+	nm_assert (!!candidate == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head,
+	                                                    connection,
+	                                                    _connections_lst));
+	return !!candidate;
 }
 
 const GSList *
@@ -500,7 +519,7 @@ nm_settings_get_unmanaged_specs (NMSettings *self)
 }
 
 static NMSettingsPlugin *
-get_plugin (NMSettings *self, guint32 capability)
+get_plugin (NMSettings *self, NMSettingsPluginCapabilities capability)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
@@ -677,8 +696,8 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			continue;
 		}
 
-		if (!strcmp (pname, "ifcfg-suse")) {
-			_LOGW ("skipping deprecated plugin ifcfg-suse");
+		if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet")) {
+			_LOGW ("skipping deprecated plugin %s", pname);
 			continue;
 		}
 
@@ -806,7 +825,6 @@ connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer
 
 static void
 connection_flags_changed (NMSettingsConnection *connection,
-                          GParamSpec *pspec,
                           gpointer user_data)
 {
 	g_signal_emit (NM_SETTINGS (user_data),
@@ -820,11 +838,20 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 {
 	NMSettings *self = NM_SETTINGS (user_data);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	const char *cpath = nm_connection_get_path (NM_CONNECTION (connection));
+	NMDevice *device;
 
-	if (!g_hash_table_lookup (priv->connections, cpath))
-		g_return_if_reached ();
-	g_object_ref (connection);
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
+	g_return_if_fail (!c_list_is_empty (&connection->_connections_lst));
+	nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
+
+	/* When the default wired connection is removed (either deleted or saved to
+	 * a new persistent connection by a plugin), write the MAC address of the
+	 * wired device to the config file and don't create a new default wired
+	 * connection for that device again.
+	 */
+	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
+	if (device)
+		default_wired_clear_tag (self, device, connection, TRUE);
 
 	/* Disconnect signal handlers, as plugins might still keep references
 	 * to the connection (and thus the signal handlers would still be live)
@@ -836,23 +863,30 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self);
 	if (!priv->startup_complete)
 		g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
-	g_object_unref (self);
 
 	/* Forget about the connection internally */
-	g_hash_table_remove (priv->connections, (gpointer) cpath);
-	g_clear_pointer (&priv->connections_cached_list, g_free);
+	_clear_connections_cached_list (priv);
+	priv->connections_len--;
+	c_list_unlink (&connection->_connections_lst);
+
+	if (priv->connections_loaded) {
+		_notify (self, PROP_CONNECTIONS);
 
-	/* Notify D-Bus */
-	g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
+		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+		                            &interface_info_settings,
+		                            &signal_info_connection_removed,
+		                            "(o)",
+		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
+	}
 
-	/* Re-emit for listeners like NMPolicy */
-	_notify (self, PROP_CONNECTIONS);
-	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (connection)))
-		nm_exported_object_unexport (NM_EXPORTED_OBJECT (connection));
+	nm_dbus_object_unexport (NM_DBUS_OBJECT (connection));
 
-	check_startup_complete (self);
+	if (priv->connections_loaded)
+		g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
 
 	g_object_unref (connection);
+
+	check_startup_complete (self);
 }
 
 #define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
@@ -900,19 +934,16 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GError *error = NULL;
-	GHashTableIter iter;
-	gpointer data;
 	const char *path;
 	NMSettingsConnection *existing;
 
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
-	g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
+	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (connection)));
 
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, &data)) {
-		/* prevent duplicates */
-		if (data == connection)
-			return;
+	/* prevent duplicates */
+	if (!c_list_is_empty (&connection->_connections_lst)) {
+		nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
+		return;
 	}
 
 	if (!nm_connection_normalize (NM_CONNECTION (connection), NULL, NULL, &error)) {
@@ -944,20 +975,19 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	/* Read seen-bssids from look-aside file and put it into the connection's data */
 	nm_settings_connection_read_and_fill_seen_bssids (connection);
 
-	/* Ensure it's initial visibility is up-to-date */
+	/* Ensure its initial visibility is up-to-date */
 	nm_settings_connection_recheck_visibility (connection);
 
 	/* Evil openconnect migration hack */
 	openconnect_migrate_hack (NM_CONNECTION (connection));
 
-	g_object_ref (self);
 	/* This one unexports the connection, it needs to run late to give the active
 	 * connection a chance to deal with its reference to this settings connection. */
 	g_signal_connect_after (connection, NM_SETTINGS_CONNECTION_REMOVED,
 	                        G_CALLBACK (connection_removed), self);
 	g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (connection_updated), self);
-	g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FLAGS,
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
 	                  G_CALLBACK (connection_flags_changed),
 	                  self);
 	if (!priv->startup_complete) {
@@ -966,28 +996,29 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 		                  self);
 	}
 
-	/* Export the connection over D-Bus */
-	g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
-	path = nm_exported_object_export (NM_EXPORTED_OBJECT (connection));
-	nm_connection_set_path (NM_CONNECTION (connection), path);
+	_clear_connections_cached_list (priv);
 
-	g_hash_table_insert (priv->connections,
-	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)),
-	                     g_object_ref (connection));
-	g_clear_pointer (&priv->connections_cached_list, g_free);
+	g_object_ref (connection);
+	priv->connections_len++;
+	c_list_link_tail (&priv->connections_lst_head, &connection->_connections_lst);
+
+	path = nm_dbus_object_export (NM_DBUS_OBJECT (connection));
 
-	nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ");
+	nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ",
+	                              path);
 
 	/* Only emit the individual connection-added signal after connections
 	 * have been initially loaded.
 	 */
 	if (priv->connections_loaded) {
-		/* Internal added signal */
+		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+		                            &interface_info_settings,
+		                            &signal_info_new_connection,
+		                            "(o)",
+		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
+
 		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
 		_notify (self, PROP_CONNECTIONS);
-
-		/* Exported D-Bus signal */
-		g_signal_emit (self, signals[NEW_CONNECTION], 0, connection);
 	}
 
 	nm_settings_connection_added (connection);
@@ -1035,14 +1066,14 @@ nm_settings_add_connection (NMSettings *self,
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 	NMSettingsConnection *added = NULL;
-	GHashTableIter citer;
-	NMConnection *candidate = NULL;
+	NMSettingsConnection *candidate = NULL;
+	const char *uuid;
+
+	uuid = nm_connection_get_uuid (connection);
 
 	/* Make sure a connection with this UUID doesn't already exist */
-	g_hash_table_iter_init (&citer, priv->connections);
-	while (g_hash_table_iter_next (&citer, NULL, (gpointer *) &candidate)) {
-		if (g_strcmp0 (nm_connection_get_uuid (connection),
-		               nm_connection_get_uuid (candidate)) == 0) {
+	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
+		if (nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate)))) {
 			g_set_error_literal (error,
 			                     NM_SETTINGS_ERROR,
 			                     NM_SETTINGS_ERROR_UUID_EXISTS,
@@ -1099,7 +1130,7 @@ send_agent_owned_secrets (NMSettings *self,
                           NMAuthSubject *subject)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	NMConnection *for_agent;
+	gs_unref_object NMConnection *for_agent = NULL;
 
 	/* Dupe the connection so we can clear out non-agent-owned secrets,
 	 * as agent-owned secrets are the only ones we send back to be saved.
@@ -1110,10 +1141,9 @@ send_agent_owned_secrets (NMSettings *self,
 	                                        secrets_filter_cb,
 	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
 	nm_agent_manager_save_secrets (priv->agent_mgr,
-	                               nm_connection_get_path (NM_CONNECTION (connection)),
+	                               nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)),
 	                               for_agent,
 	                               subject);
-	g_object_unref (for_agent);
 }
 
 static void
@@ -1170,7 +1200,7 @@ pk_add_cb (NMAuthChain *chain,
 		send_agent_owned_secrets (self, added, subject);
 
 	g_clear_error (&error);
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 /* FIXME: remove if/when kernel supports adhoc wpa */
@@ -1218,7 +1248,6 @@ nm_settings_add_connection_dbus (NMSettings *self,
 	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
 	GError *error = NULL, *tmp_error = NULL;
-	char *error_desc = NULL;
 	const char *perm;
 
 	g_return_if_fail (connection != NULL);
@@ -1261,18 +1290,12 @@ nm_settings_add_connection_dbus (NMSettings *self,
 		goto done;
 	}
 
-	/* Ensure the caller's username exists in the connection's permissions,
-	 * or that the permissions is empty (ie, visible by everyone).
-	 */
-	if (!nm_auth_is_subject_in_acl (connection,
-	                                subject,
-	                                &error_desc)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		g_free (error_desc);
+	if (!nm_auth_is_subject_in_acl_set_error (connection,
+	                                          subject,
+	                                          NM_SETTINGS_ERROR,
+	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
+	                                          &error))
 		goto done;
-	}
 
 	/* If the caller is the only user in the connection's permissions, then
 	 * we use the 'modify.own' permission instead of 'modify.system'.  If the
@@ -1312,30 +1335,30 @@ done:
 }
 
 static void
-impl_settings_add_connection_add_cb (NMSettings *self,
-                                     NMSettingsConnection *connection,
-                                     GError *error,
-                                     GDBusMethodInvocation *context,
-                                     NMAuthSubject *subject,
-                                     gpointer user_data)
+settings_add_connection_add_cb (NMSettings *self,
+                                NMSettingsConnection *connection,
+                                GError *error,
+                                GDBusMethodInvocation *context,
+                                NMAuthSubject *subject,
+                                gpointer user_data)
 {
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, NULL, subject, error->message);
 	} else {
-		g_dbus_method_invocation_return_value (
-		    context,
-		    g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
+		g_dbus_method_invocation_return_value (context,
+		                                       g_variant_new ("(o)",
+		                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))));
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL,
 		                            subject, NULL);
 	}
 }
 
 static void
-impl_settings_add_connection_helper (NMSettings *self,
-                                     GDBusMethodInvocation *context,
-                                     GVariant *settings,
-                                     gboolean save_to_disk)
+settings_add_connection_helper (NMSettings *self,
+                                GDBusMethodInvocation *context,
+                                GVariant *settings,
+                                gboolean save_to_disk)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	GError *error = NULL;
@@ -1355,77 +1378,111 @@ impl_settings_add_connection_helper (NMSettings *self,
 	                                 connection,
 	                                 save_to_disk,
 	                                 context,
-	                                 impl_settings_add_connection_add_cb,
+	                                 settings_add_connection_add_cb,
 	                                 NULL);
 }
 
 static void
-impl_settings_add_connection (NMSettings *self,
-                              GDBusMethodInvocation *context,
-                              GVariant *settings)
+impl_settings_add_connection (NMDBusObject *obj,
+                              const NMDBusInterfaceInfoExtended *interface_info,
+                              const NMDBusMethodInfoExtended *method_info,
+                              GDBusConnection *connection,
+                              const char *sender,
+                              GDBusMethodInvocation *invocation,
+                              GVariant *parameters)
 {
-	impl_settings_add_connection_helper (self, context, settings, TRUE);
+	NMSettings *self = NM_SETTINGS (obj);
+	gs_unref_variant GVariant *settings = NULL;
+
+	g_variant_get (parameters, "(@a{sa{sv}})", &settings);
+	settings_add_connection_helper (self, invocation, settings, TRUE);
 }
 
 static void
-impl_settings_add_connection_unsaved (NMSettings *self,
-                                      GDBusMethodInvocation *context,
-                                      GVariant *settings)
+impl_settings_add_connection_unsaved (NMDBusObject *obj,
+                                      const NMDBusInterfaceInfoExtended *interface_info,
+                                      const NMDBusMethodInfoExtended *method_info,
+                                      GDBusConnection *connection,
+                                      const char *sender,
+                                      GDBusMethodInvocation *invocation,
+                                      GVariant *parameters)
 {
-	impl_settings_add_connection_helper (self, context, settings, FALSE);
+	NMSettings *self = NM_SETTINGS (obj);
+	gs_unref_variant GVariant *settings = NULL;
+
+	g_variant_get (parameters, "(@a{sa{sv}})", &settings);
+	settings_add_connection_helper (self, invocation, settings, FALSE);
 }
 
 static void
-impl_settings_load_connections (NMSettings *self,
-                                GDBusMethodInvocation *context,
-                                char **filenames)
+impl_settings_load_connections (NMDBusObject *obj,
+                                const NMDBusInterfaceInfoExtended *interface_info,
+                                const NMDBusMethodInfoExtended *method_info,
+                                GDBusConnection *connection,
+                                const char *sender,
+                                GDBusMethodInvocation *invocation,
+                                GVariant *parameters)
 {
+	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GPtrArray *failures;
+	gs_unref_ptrarray GPtrArray *failures = NULL;
 	GSList *iter;
-	int i;
+	guint i;
+	gs_free const char **filenames = NULL;
+
+	g_variant_get (parameters, "(^a&s)", &filenames);
 
 	/* The permission is already enforced by the D-Bus daemon, but we ensure
 	 * that the caller is still alive so that clients are forced to wait and
 	 * we'll be able to switch to polkit without breaking behavior.
 	 */
-	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
-	                                context,
-	                                G_MAXULONG,
-	                                NM_SETTINGS_ERROR,
-	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
+	if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (obj),
+	                                 invocation,
+	                                 G_MAXULONG,
+	                                 NM_SETTINGS_ERROR,
+	                                 NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
-	failures = g_ptr_array_new ();
-
-	for (i = 0; filenames[i]; i++) {
-		for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
-			NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+	if (filenames) {
+		for (i = 0; filenames[i]; i++) {
+			for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+				NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
 
-			if (nm_settings_plugin_load_connection (plugin, filenames[i]))
-				break;
-		}
+				if (nm_settings_plugin_load_connection (plugin, filenames[i]))
+					break;
+			}
 
-		if (!iter) {
-			if (!g_path_is_absolute (filenames[i]))
-				_LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
-			g_ptr_array_add (failures, (char *) filenames[i]);
+			if (!iter) {
+				if (!g_path_is_absolute (filenames[i]))
+					_LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
+				if (!failures)
+					failures = g_ptr_array_new ();
+				g_ptr_array_add (failures, (char *) filenames[i]);
+			}
 		}
 	}
 
-	g_ptr_array_add (failures, NULL);
-	g_dbus_method_invocation_return_value (
-		context,
-		g_variant_new ("(b^as)",
-		               failures->len == 1,
-		               failures->pdata));
-	g_ptr_array_unref (failures);
+	if (failures)
+		g_ptr_array_add (failures, NULL);
+
+	g_dbus_method_invocation_return_value (invocation,
+	                                       g_variant_new ("(b^as)",
+	                                                      (gboolean) (!!failures),
+	                                                      failures
+	                                                        ? (const char **) failures->pdata
+	                                                        : NM_PTRARRAY_EMPTY (const char *)));
 }
 
 static void
-impl_settings_reload_connections (NMSettings *self,
-                                  GDBusMethodInvocation *context)
+impl_settings_reload_connections (NMDBusObject *obj,
+                                  const NMDBusInterfaceInfoExtended *interface_info,
+                                  const NMDBusMethodInfoExtended *method_info,
+                                  GDBusConnection *connection,
+                                  const char *sender,
+                                  GDBusMethodInvocation *invocation,
+                                  GVariant *parameters)
 {
+	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
@@ -1433,11 +1490,11 @@ impl_settings_reload_connections (NMSettings *self,
 	 * that the caller is still alive so that clients are forced to wait and
 	 * we'll be able to switch to polkit without breaking behavior.
 	 */
-	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
-	                                context,
-	                                G_MAXULONG,
-	                                NM_SETTINGS_ERROR,
-	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
+	if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (obj),
+	                                 invocation,
+	                                 G_MAXULONG,
+	                                 NM_SETTINGS_ERROR,
+	                                 NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
 	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
@@ -1446,7 +1503,7 @@ impl_settings_reload_connections (NMSettings *self,
 		nm_settings_plugin_reload_connections (plugin);
 	}
 
-	g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE));
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
 }
 
 /*****************************************************************************/
@@ -1494,41 +1551,46 @@ pk_hostname_cb (NMAuthChain *chain,
 	else
 		g_dbus_method_invocation_return_value (context, NULL);
 
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_settings_save_hostname (NMSettings *self,
-                             GDBusMethodInvocation *context,
-                             const char *hostname)
+impl_settings_save_hostname (NMDBusObject *obj,
+                             const NMDBusInterfaceInfoExtended *interface_info,
+                             const NMDBusMethodInfoExtended *method_info,
+                             GDBusConnection *connection,
+                             const char *sender,
+                             GDBusMethodInvocation *invocation,
+                             GVariant *parameters)
 {
+	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	NMAuthChain *chain;
-	GError *error = NULL;
+	const char *hostname;
+
+	g_variant_get (parameters, "(&s)", &hostname);
 
 	/* Minimal validation of the hostname */
 	if (!nm_hostname_manager_validate_hostname (hostname)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_INVALID_HOSTNAME,
-		                             "The hostname was too long or contained invalid characters.");
-		goto done;
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_SETTINGS_ERROR,
+		                                               NM_SETTINGS_ERROR_INVALID_HOSTNAME,
+		                                               "The hostname was too long or contained invalid characters.");
+		return;
 	}
 
-	chain = nm_auth_chain_new_context (context, pk_hostname_cb, self);
+	chain = nm_auth_chain_new_context (invocation, pk_hostname_cb, self);
 	if (!chain) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             "Unable to authenticate the request.");
-		goto done;
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_SETTINGS_ERROR,
+		                                               NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate the request.");
+		return;
 	}
 
 	priv->auths = g_slist_append (priv->auths, chain);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE);
 	nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free);
-
-done:
-	if (error)
-		g_dbus_method_invocation_take_error (context, error);
 }
 
 /*****************************************************************************/
@@ -1537,27 +1599,24 @@ static gboolean
 have_connection_for_device (NMSettings *self, NMDevice *device)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GHashTableIter iter;
-	gpointer data;
 	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 	const char *setting_hwaddr;
 	const char *perm_hw_addr;
+	NMSettingsConnection *connection;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
 
 	perm_hw_addr = nm_device_get_permanent_hw_address (device);
 
 	/* Find a wired connection locked to the given MAC address, if any */
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, &data)) {
-		NMConnection *connection = NM_CONNECTION (data);
+	c_list_for_each_entry (connection, &priv->connections_lst_head, _connections_lst) {
 		const char *ctype, *iface;
 
-		if (!nm_device_check_connection_compatible (device, connection))
+		if (!nm_device_check_connection_compatible (device, NM_CONNECTION (connection)))
 			continue;
 
-		s_con = nm_connection_get_setting_connection (connection);
+		s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 
 		iface = nm_setting_connection_get_interface_name (s_con);
 		if (iface && strcmp (iface, nm_device_get_iface (device)) != 0)
@@ -1568,7 +1627,7 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 		    && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
 			continue;
 
-		s_wired = nm_connection_get_setting_wired (connection);
+		s_wired = nm_connection_get_setting_wired (NM_CONNECTION (connection));
 
 		if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
 			/* No wired setting; therefore the PPPoE connection applies to any device */
@@ -1596,26 +1655,6 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	return FALSE;
 }
 
-static void default_wired_clear_tag (NMSettings *self,
-                                     NMDevice *device,
-                                     NMSettingsConnection *connection,
-                                     gboolean add_to_no_auto_default);
-
-static void
-default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self)
-{
-	NMDevice *device;
-
-	/* When the default wired connection is removed (either deleted or saved to
-	 * a new persistent connection by a plugin), write the MAC address of the
-	 * wired device to the config file and don't create a new default wired
-	 * connection for that device again.
-	 */
-	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
-	if (device)
-		default_wired_clear_tag (self, device, connection, TRUE);
-}
-
 static void
 default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, gboolean by_user, NMSettings *self)
 {
@@ -1648,7 +1687,6 @@ default_wired_clear_tag (NMSettings *self,
 	g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
 	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
 
-	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
 
 	if (add_to_no_auto_default)
@@ -1700,8 +1738,6 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 
 	g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
-	g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
-	                  G_CALLBACK (default_wired_connection_removed_cb), self);
 
 	_LOGI ("(%s): created default wired connection '%s'",
 	       nm_device_get_iface (device),
@@ -1774,10 +1810,8 @@ nm_settings_start (NMSettings *self, GError **error)
 	/* Load the plugins; fail if a plugin is not found. */
 	plugins = nm_config_data_get_plugins (nm_config_get_data_orig (priv->config), TRUE);
 
-	if (!load_plugins (self, (const char **) plugins, error)) {
-		g_object_unref (self);
+	if (!load_plugins (self, (const char **) plugins, error))
 		return FALSE;
-	}
 
 	load_connections (self);
 	check_startup_complete (self);
@@ -1802,18 +1836,19 @@ get_property (GObject *object, guint prop_id,
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	const GSList *specs, *iter;
-	GHashTableIter citer;
-	GPtrArray *array;
-	const char *path;
+	guint i;
+	char **strvs;
+	const char **strv;
 
 	switch (prop_id) {
 	case PROP_UNMANAGED_SPECS:
-		array = g_ptr_array_new ();
 		specs = nm_settings_get_unmanaged_specs (self);
-		for (iter = specs; iter; iter = g_slist_next (iter))
-			g_ptr_array_add (array, g_strdup (iter->data));
-		g_ptr_array_add (array, NULL);
-		g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
+		strvs = g_new (char *, g_slist_length ((GSList *) specs) + 1);
+		i = 0;
+		for (iter = specs; iter; iter = iter->next)
+			strvs[i++] = g_strdup (iter->data);
+		strvs[i] = NULL;
+		g_value_take_boxed (value, strvs);
 		break;
 	case PROP_HOSTNAME:
 		g_value_set_string (value,
@@ -1825,12 +1860,14 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS));
 		break;
 	case PROP_CONNECTIONS:
-		array = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
-		g_hash_table_iter_init (&citer, priv->connections);
-		while (g_hash_table_iter_next (&citer, (gpointer) &path, NULL))
-			g_ptr_array_add (array, g_strdup (path));
-		g_ptr_array_add (array, NULL);
-		g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
+		if (priv->connections_loaded) {
+			strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
+			                                          priv->connections_len,
+			                                          G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
+			                                          TRUE);
+			g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
+		} else
+			g_value_set_boxed (value, NULL);
 		break;
 	case PROP_STARTUP_COMPLETE:
 		g_value_set_boolean (value, nm_settings_get_startup_complete (self));
@@ -1848,7 +1885,7 @@ nm_settings_init (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
+	c_list_init (&priv->connections_lst_head);
 
 	priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
 	priv->config = g_object_ref (nm_config_get ());
@@ -1866,11 +1903,9 @@ dispose (GObject *object)
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref);
+	g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_destroy);
 	priv->auths = NULL;
 
-	g_object_unref (priv->agent_mgr);
-
 	if (priv->hostname_manager) {
 		g_signal_handlers_disconnect_by_func (priv->hostname_manager,
 		                                      G_CALLBACK (_hostname_changed_cb),
@@ -1887,26 +1922,139 @@ finalize (GObject *object)
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	g_hash_table_destroy (priv->connections);
-	g_clear_pointer (&priv->connections_cached_list, g_free);
+	_clear_connections_cached_list (priv);
+
+	nm_assert (c_list_is_empty (&priv->connections_lst_head));
 
 	g_slist_free_full (priv->unmanaged_specs, g_free);
 	g_slist_free_full (priv->unrecognized_specs, g_free);
 
 	g_slist_free_full (priv->plugins, g_object_unref);
 
+	g_clear_object (&priv->agent_mgr);
+
 	g_clear_object (&priv->config);
 
 	G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
 }
 
+static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
+	"NewConnection",
+	.args = NM_DEFINE_GDBUS_ARG_INFOS (
+		NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"),
+	),
+);
+
+static const GDBusSignalInfo signal_info_connection_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
+	"ConnectionRemoved",
+	.args = NM_DEFINE_GDBUS_ARG_INFOS (
+		NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"),
+	),
+);
+
+static const NMDBusInterfaceInfoExtended interface_info_settings = {
+	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
+		NM_DBUS_INTERFACE_SETTINGS,
+		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"ListConnections",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connections", "ao"),
+					),
+				),
+				.handle = impl_settings_list_connections,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"GetConnectionByUuid",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("uuid", "s"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"),
+					),
+				),
+				.handle = impl_settings_get_connection_by_uuid,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"AddConnection",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
+					),
+				),
+				.handle = impl_settings_add_connection,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"AddConnectionUnsaved",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
+					),
+				),
+				.handle = impl_settings_add_connection_unsaved,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"LoadConnections",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("filenames", "as"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("status", "b"),
+						NM_DEFINE_GDBUS_ARG_INFO ("failures", "as"),
+					),
+				),
+				.handle = impl_settings_load_connections,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"ReloadConnections",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("status", "b"),
+					),
+				),
+				.handle = impl_settings_reload_connections,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"SaveHostname",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("hostname", "s"),
+					),
+				),
+				.handle = impl_settings_save_hostname,
+			),
+		),
+		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
+			&nm_signal_info_property_changed_legacy,
+			&signal_info_new_connection,
+			&signal_info_connection_removed,
+		),
+		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connections", "ao", NM_SETTINGS_CONNECTIONS),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Hostname",    "s",  NM_SETTINGS_HOSTNAME),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CanModify",   "b",  NM_SETTINGS_CAN_MODIFY),
+		),
+	),
+	.legacy_property_changed = TRUE,
+};
+
 static void
 nm_settings_class_init (NMSettingsClass *class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (class);
-	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
+	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (class);
 
-	exported_object_class->export_path = NM_DBUS_PATH_SETTINGS;
+	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH_SETTINGS);
+	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings);
 
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
@@ -1975,23 +2123,4 @@ nm_settings_class_init (NMSettingsClass *class)
 	                  0, NULL, NULL,
 	                  g_cclosure_marshal_VOID__OBJECT,
 	                  G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
-
-	signals[NEW_CONNECTION] =
-	    g_signal_new ("new-connection",
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__OBJECT,
-	                  G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
-
-	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
-	                                        NMDBUS_TYPE_SETTINGS_SKELETON,
-	                                        "ListConnections", impl_settings_list_connections,
-	                                        "GetConnectionByUuid", impl_settings_get_connection_by_uuid,
-	                                        "AddConnection", impl_settings_add_connection,
-	                                        "AddConnectionUnsaved", impl_settings_add_connection_unsaved,
-	                                        "LoadConnections", impl_settings_load_connections,
-	                                        "ReloadConnections", impl_settings_reload_connections,
-	                                        "SaveHostname", impl_settings_save_hostname,
-	                                        NULL);
 }
-