about summary refs log tree commit diff
path: root/src/settings/nm-settings.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 03:04:17 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 03:04:17 +0200
commit3ce667b6ee6b86291bbe60ef93cba7f11a5c5400 (patch)
treeaf3daa7221e898b72f3de26eb2cf0c1b1c8661b0 /src/settings/nm-settings.c
parentf02d31d95af29678092d1ff01f714621bc9dcc0f (diff)
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Merge tag 'upstream/0.9.10.0'
Upstream version 0.9.10.0
Diffstat (limited to 'src/settings/nm-settings.c')
-rw-r--r--src/settings/nm-settings.c1223
1 files changed, 611 insertions, 612 deletions
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 71009a0c..e90776ab 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -28,7 +28,6 @@
 #include <unistd.h>
 #include <string.h>
 #include <gmodule.h>
-#include <net/if_arp.h>
 #include <pwd.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib-lowlevel.h>
@@ -54,13 +53,11 @@
 #include <nm-setting-bond.h>
 #include <nm-utils.h>
 
-#include "../nm-device-ethernet.h"
-#include "../nm-device-wired.h"
+#include "nm-device-ethernet.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-settings.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-error.h"
-#include "nm-default-wired-connection.h"
 #include "nm-logging.h"
 #include "nm-dbus-manager.h"
 #include "nm-manager-auth.h"
@@ -69,8 +66,8 @@
 #include "nm-agent-manager.h"
 #include "nm-settings-utils.h"
 #include "nm-connection-provider.h"
-
-#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default"
+#include "nm-config.h"
+#include "NetworkManagerUtils.h"
 
 /* LINKER CRACKROCK */
 #define EXPORT(sym) void * __export_##sym = &sym;
@@ -94,15 +91,25 @@ static gboolean impl_settings_list_connections (NMSettings *self,
                                                 GPtrArray **connections,
                                                 GError **error);
 
-static gboolean impl_settings_get_connection_by_uuid (NMSettings *self,
-                                                      const char *uuid,
-                                                      char **out_object_path,
-                                                      GError **error);
+static void impl_settings_get_connection_by_uuid (NMSettings *self,
+                                                  const char *uuid,
+                                                  DBusGMethodInvocation *context);
 
 static void impl_settings_add_connection (NMSettings *self,
                                           GHashTable *settings,
                                           DBusGMethodInvocation *context);
 
+static void impl_settings_add_connection_unsaved (NMSettings *self,
+                                                  GHashTable *settings,
+                                                  DBusGMethodInvocation *context);
+
+static void impl_settings_load_connections (NMSettings *self,
+                                            char **filenames,
+                                            DBusGMethodInvocation *context);
+
+static void impl_settings_reload_connections (NMSettings *self,
+                                              DBusGMethodInvocation *context);
+
 static void impl_settings_save_hostname (NMSettings *self,
                                          const char *hostname,
                                          DBusGMethodInvocation *context);
@@ -110,6 +117,7 @@ static void impl_settings_save_hostname (NMSettings *self,
 #include "nm-settings-glue.h"
 
 static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data);
+static void unrecognized_specs_changed (NMSystemConfigInterface *config, gpointer user_data);
 
 static void connection_provider_init (NMConnectionProvider *cp_class);
 
@@ -119,19 +127,19 @@ G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, G_TYPE_OBJECT, 0,
 
 typedef struct {
 	NMDBusManager *dbus_mgr;
-	DBusGConnection *bus;
 
 	NMAgentManager *agent_mgr;
 
-	char *config_file;
+	NMConfig *config;
 
-	NMSessionMonitor *session_monitor;
 	GSList *auths;
 
 	GSList *plugins;
 	gboolean connections_loaded;
 	GHashTable *connections;
 	GSList *unmanaged_specs;
+	GSList *unrecognized_specs;
+	GSList *get_connections_cache;
 } NMSettingsPrivate;
 
 #define NM_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTINGS, NMSettingsPrivate))
@@ -140,9 +148,9 @@ enum {
 	PROPERTIES_CHANGED,
 	CONNECTION_ADDED,
 	CONNECTION_UPDATED,
+	CONNECTION_UPDATED_BY_USER,
 	CONNECTION_REMOVED,
 	CONNECTION_VISIBILITY_CHANGED,
-	CONNECTIONS_LOADED,
 	AGENT_REGISTERED,
 
 	NEW_CONNECTION, /* exported, not used internally */
@@ -155,19 +163,25 @@ enum {
 	PROP_UNMANAGED_SPECS,
 	PROP_HOSTNAME,
 	PROP_CAN_MODIFY,
+	PROP_CONNECTIONS,
 
 	LAST_PROP
 };
 
 static void
+plugin_connection_added (NMSystemConfigInterface *config,
+                         NMSettingsConnection *connection,
+                         gpointer user_data)
+{
+	claim_connection (NM_SETTINGS (user_data), connection, TRUE);
+}
+
+static void
 load_connections (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
-	if (priv->connections_loaded)
-		return;
-
 	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
 		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
 		GSList *plugin_connections;
@@ -183,15 +197,19 @@ load_connections (NMSettings *self)
 			claim_connection (self, NM_SETTINGS_CONNECTION (elt->data), TRUE);
 
 		g_slist_free (plugin_connections);
+
+		g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED,
+		                  G_CALLBACK (plugin_connection_added), self);
+		g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED,
+		                  G_CALLBACK (unmanaged_specs_changed), self);
+		g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNRECOGNIZED_SPECS_CHANGED,
+		                  G_CALLBACK (unrecognized_specs_changed), self);
 	}
 
 	priv->connections_loaded = TRUE;
 
-	/* FIXME: Bad hack */
 	unmanaged_specs_changed (NULL, self);
-
-	g_signal_emit (self, signals[CONNECTIONS_LOADED], 0);
-	g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTIONS_LOADED);
+	unrecognized_specs_changed (NULL, self);
 }
 
 void
@@ -203,14 +221,11 @@ nm_settings_for_each_connection (NMSettings *self,
 	GHashTableIter iter;
 	gpointer data;
 
-	g_return_if_fail (self != NULL);
 	g_return_if_fail (NM_IS_SETTINGS (self));
 	g_return_if_fail (for_each_func != NULL);
 	
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	load_connections (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);
@@ -225,8 +240,6 @@ impl_settings_list_connections (NMSettings *self,
 	GHashTableIter iter;
 	gpointer key;
 
-	load_connections (self);
-
 	*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))
@@ -241,14 +254,11 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
 	NMSettingsConnection *candidate;
 	GHashTableIter iter;
 
-	g_return_val_if_fail (self != NULL, NULL);
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	load_connections (self);
-
 	g_hash_table_iter_init (&iter, priv->connections);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
 		if (g_strcmp0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate))) == 0)
@@ -258,25 +268,52 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
 	return NULL;
 }
 
-static gboolean
+static void
 impl_settings_get_connection_by_uuid (NMSettings *self,
                                       const char *uuid,
-                                      char **out_object_path,
-                                      GError **error)
+                                      DBusGMethodInvocation *context)
 {
 	NMSettingsConnection *connection = NULL;
+	NMAuthSubject *subject = NULL;
+	GError *error = NULL;
+	char *error_desc = NULL;
 
 	connection = nm_settings_get_connection_by_uuid (self, uuid);
-	if (connection)
-		*out_object_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection)));
-	else {
-		g_set_error_literal (error,
-		                     NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                     "No connection with the UUID was found.");
+	if (!connection) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		                             "No connection with the UUID was found.");
+		goto error;
 	}
 
-	return !!connection;
+	subject = nm_auth_subject_new_from_context (context);
+	if (!subject) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             "Unable to determine UID of request.");
+		goto error;
+	}
+
+	if (!nm_auth_uid_in_acl (NM_CONNECTION (connection),
+	                         nm_session_monitor_get (),
+	                         nm_auth_subject_get_uid (subject),
+	                         &error_desc)) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
+		goto error;
+	}
+
+	g_clear_object (&subject);
+	dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection)));
+	return;
+
+error:
+	g_assert (error);
+	dbus_g_method_return_error (context, error);
+	g_error_free (error);
+	g_clear_object (&subject);
 }
 
 static int
@@ -308,8 +345,10 @@ connection_sort (gconstpointer pa, gconstpointer pb)
 	return 1;
 }
 
-/* Returns a list of NMSettingsConnections.  Caller must free the list with
- * g_slist_free().
+/* Returns a list of NMSettingsConnections.
+ * The list is sorted in the order suitable for auto-connecting, i.e.
+ * first go connections with autoconnect=yes and most recent timestamp.
+ * Caller must free the list with g_slist_free().
  */
 GSList *
 nm_settings_get_connections (NMSettings *self)
@@ -331,27 +370,14 @@ nm_settings_get_connection_by_path (NMSettings *self, const char *path)
 {
 	NMSettingsPrivate *priv;
 
-	g_return_val_if_fail (self != NULL, NULL);
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 	g_return_val_if_fail (path != NULL, NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	load_connections (self);
-
 	return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path);
 }
 
-static void
-clear_unmanaged_specs (NMSettings *self)
-{
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	g_slist_foreach (priv->unmanaged_specs, (GFunc) g_free, NULL);
-	g_slist_free (priv->unmanaged_specs);
-	priv->unmanaged_specs = NULL;
-}
-
 static char*
 uscore_to_wincaps (const char *uscore)
 {
@@ -402,7 +428,6 @@ nm_settings_get_unmanaged_specs (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	load_connections (self);
 	return priv->unmanaged_specs;
 }
 
@@ -452,56 +477,67 @@ nm_settings_get_hostname (NMSettings *self)
 	return NULL;
 }
 
-static void
-plugin_connection_added (NMSystemConfigInterface *config,
-                         NMSettingsConnection *connection,
-                         gpointer user_data)
-{
-	claim_connection (NM_SETTINGS (user_data), connection, TRUE);
-}
-
 static gboolean
-find_unmanaged_device (NMSettings *self, const char *needle)
+find_spec (GSList *spec_list, const char *spec)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
-	for (iter = priv->unmanaged_specs; iter; iter = g_slist_next (iter)) {
-		if (!strcmp ((const char *) iter->data, needle))
+	for (iter = spec_list; iter; iter = g_slist_next (iter)) {
+		if (!strcmp ((const char *) iter->data, spec))
 			return TRUE;
 	}
 	return FALSE;
 }
 
 static void
-unmanaged_specs_changed (NMSystemConfigInterface *config,
-                         gpointer user_data)
+update_specs (NMSettings *self, GSList **specs_ptr,
+              GSList * (*get_specs_func) (NMSystemConfigInterface *))
 {
-	NMSettings *self = NM_SETTINGS (user_data);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
-	clear_unmanaged_specs (self);
+	g_slist_free_full (*specs_ptr, g_free);
+	*specs_ptr = NULL;
 
-	/* Ask all the plugins for their unmanaged specs */
 	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
 		GSList *specs, *specs_iter;
 
-		specs = nm_system_config_interface_get_unmanaged_specs (NM_SYSTEM_CONFIG_INTERFACE (iter->data));
+		specs = get_specs_func (NM_SYSTEM_CONFIG_INTERFACE (iter->data));
 		for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) {
-			if (!find_unmanaged_device (self, (const char *) specs_iter->data)) {
-				priv->unmanaged_specs = g_slist_prepend (priv->unmanaged_specs, specs_iter->data);
+			if (!find_spec (*specs_ptr, (const char *) specs_iter->data)) {
+				*specs_ptr = g_slist_prepend (*specs_ptr, specs_iter->data);
 			} else
 				g_free (specs_iter->data);
 		}
 
 		g_slist_free (specs);
 	}
+}
 
+static void
+unmanaged_specs_changed (NMSystemConfigInterface *config,
+                         gpointer user_data)
+{
+	NMSettings *self = NM_SETTINGS (user_data);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	update_specs (self, &priv->unmanaged_specs,
+	              nm_system_config_interface_get_unmanaged_specs);
 	g_object_notify (G_OBJECT (self), NM_SETTINGS_UNMANAGED_SPECS);
 }
 
 static void
+unrecognized_specs_changed (NMSystemConfigInterface *config,
+                               gpointer user_data)
+{
+	NMSettings *self = NM_SETTINGS (user_data);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	update_specs (self, &priv->unrecognized_specs,
+	              nm_system_config_interface_get_unrecognized_specs);
+}
+
+static void
 hostname_changed (NMSystemConfigInterface *config,
                   GParamSpec *pspec,
                   gpointer user_data)
@@ -523,8 +559,6 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
 
 	priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
 
-	g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED,
-	                  G_CALLBACK (plugin_connection_added), self);
 	g_signal_connect (plugin, "notify::hostname", G_CALLBACK (hostname_changed), self);
 
 	nm_system_config_interface_init (plugin, NULL);
@@ -534,9 +568,6 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
 	              NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo,
 	              NULL);
 
-	g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED,
-	                  G_CALLBACK (unmanaged_specs_changed), self);
-
 	nm_log_info (LOGD_SETTINGS, "Loaded plugin %s: %s", pname, pinfo);
 	g_free (pname);
 	g_free (pinfo);
@@ -570,10 +601,9 @@ find_plugin (GSList *list, const char *pname)
 static void
 add_keyfile_plugin (NMSettings *self)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GObject *keyfile_plugin;
 
-	keyfile_plugin = nm_settings_keyfile_plugin_new (priv->config_file);
+	keyfile_plugin = nm_settings_keyfile_plugin_new ();
 	g_assert (keyfile_plugin);
 	add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (keyfile_plugin));
 }
@@ -581,7 +611,6 @@ add_keyfile_plugin (NMSettings *self)
 static gboolean
 load_plugins (NMSettings *self, const char **plugins, GError **error)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *list = NULL;
 	const char **iter;
 	gboolean keyfile_added = FALSE;
@@ -592,7 +621,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 		char *full_name, *path;
 		const char *pname = *iter;
 		GObject *obj;
-		GObject * (*factory_func) (const char *);
+		GObject * (*factory_func) (void);
 
 		/* strip leading spaces */
 		while (g_ascii_isspace (*pname))
@@ -616,7 +645,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 		}
 
 		full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
-		path = g_module_build_path (PLUGINDIR, full_name);
+		path = g_module_build_path (NMPLUGINDIR, full_name);
 
 		plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
 		if (!plugin) {
@@ -640,7 +669,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			break;
 		}
 
-		obj = (*factory_func) (priv->config_file);
+		obj = (*factory_func) ();
 		if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) {
 			g_set_error (error, 0, 0,
 			             "Plugin '%s' returned invalid system config object.",
@@ -659,77 +688,30 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 	if (!keyfile_added)
 		add_keyfile_plugin (self);
 
-	g_slist_foreach (list, (GFunc) g_object_unref, NULL);
-	g_slist_free (list);
+	g_slist_free_full (list, g_object_unref);
 
 	return success;
 }
 
-#define REMOVED_ID_TAG "removed-id-tag"
-#define UPDATED_ID_TAG "updated-id-tag"
-#define VISIBLE_ID_TAG "visible-id-tag"
-#define UNREG_ID_TAG "unreg-id-tag"
-
 static void
-connection_removed (NMSettingsConnection *obj, gpointer user_data)
+connection_updated (NMSettingsConnection *connection, gpointer user_data)
 {
-	GObject *connection = G_OBJECT (obj);
-	guint id;
-
-	g_object_ref (connection);
-
-	/* Disconnect signal handlers, as plugins might still keep references
-	 * to the connection (and thus the signal handlers would still be live)
-	 * even after NMSettings has dropped all its references.
-	 */
-
-	id = GPOINTER_TO_UINT (g_object_get_data (connection, REMOVED_ID_TAG));
-	if (id)
-		g_signal_handler_disconnect (connection, id);
-
-	id = GPOINTER_TO_UINT (g_object_get_data (connection, UPDATED_ID_TAG));
-	if (id)
-		g_signal_handler_disconnect (connection, id);
-
-	id = GPOINTER_TO_UINT (g_object_get_data (connection, VISIBLE_ID_TAG));
-	if (id)
-		g_signal_handler_disconnect (connection, id);
-
-	/* Forget about the connection internally */
-	g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections,
-	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)));
-
 	/* Re-emit for listeners like NMPolicy */
-	g_signal_emit (NM_SETTINGS (user_data), signals[CONNECTION_REMOVED], 0, connection);
-	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_REMOVED, connection);
-
-	g_object_unref (connection);
-}
-
-static void
-connection_unregister (NMSettingsConnection *obj, gpointer user_data)
-{
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (user_data);
-	GObject *connection = G_OBJECT (obj);
-	guint id;
-
-	/* Make sure it's unregistered from the bus now that's removed */
-	dbus_g_connection_unregister_g_object (priv->bus, connection);
-
-	id = GPOINTER_TO_UINT (g_object_get_data (connection, UNREG_ID_TAG));
-	if (id)
-		g_signal_handler_disconnect (connection, id);
+	g_signal_emit (NM_SETTINGS (user_data),
+	               signals[CONNECTION_UPDATED],
+	               0,
+	               connection);
+	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection);
 }
 
 static void
-connection_updated (NMSettingsConnection *connection, gpointer user_data)
+connection_updated_by_user (NMSettingsConnection *connection, gpointer user_data)
 {
 	/* Re-emit for listeners like NMPolicy */
 	g_signal_emit (NM_SETTINGS (user_data),
-	               signals[CONNECTION_UPDATED],
+	               signals[CONNECTION_UPDATED_BY_USER],
 	               0,
 	               connection);
-	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection);
 }
 
 static void
@@ -745,6 +727,37 @@ connection_visibility_changed (NMSettingsConnection *connection,
 }
 
 static void
+connection_removed (NMSettingsConnection *connection, gpointer user_data)
+{
+	NMSettings *self = NM_SETTINGS (user_data);
+
+	g_object_ref (connection);
+
+	/* Disconnect signal handlers, as plugins might still keep references
+	 * to the connection (and thus the signal handlers would still be live)
+	 * even after NMSettings has dropped all its references.
+	 */
+
+	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
+	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
+	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated_by_user), self);
+	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_visibility_changed), self);
+
+	/* Forget about the connection internally */
+	g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections,
+	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)));
+
+	/* Notify D-Bus */
+	g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
+
+	/* Re-emit for listeners like NMPolicy */
+	g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_REMOVED, connection);
+	g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
+
+	g_object_unref (connection);
+}
+
+static void
 secret_agent_registered (NMAgentManager *agent_mgr,
                          NMSecretAgent *agent,
                          gpointer user_data)
@@ -807,7 +820,6 @@ claim_connection (NMSettings *self,
 	GHashTableIter iter;
 	gpointer data;
 	char *path;
-	guint id;
 
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
 	g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
@@ -819,6 +831,8 @@ claim_connection (NMSettings *self,
 			return;
 	}
 
+	nm_utils_normalize_connection (NM_CONNECTION (connection), TRUE);
+
 	if (!nm_connection_verify (NM_CONNECTION (connection), &error)) {
 		nm_log_warn (LOGD_SETTINGS, "plugin provided invalid connection: '%s' / '%s' invalid: %d",
 		             g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)),
@@ -839,31 +853,21 @@ claim_connection (NMSettings *self,
 	/* Evil openconnect migration hack */
 	openconnect_migrate_hack (NM_CONNECTION (connection));
 
-	id = g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
-	                       G_CALLBACK (connection_removed),
-	                       self);
-	g_object_set_data (G_OBJECT (connection), REMOVED_ID_TAG, GUINT_TO_POINTER (id));
-
-	id = g_signal_connect (connection, "unregister",
-	                       G_CALLBACK (connection_unregister),
-	                       self);
-	g_object_set_data (G_OBJECT (connection), UNREG_ID_TAG, GUINT_TO_POINTER (id));
-
-	id = g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED,
-	                       G_CALLBACK (connection_updated),
-	                       self);
-	g_object_set_data (G_OBJECT (connection), UPDATED_ID_TAG, GUINT_TO_POINTER (id));
-
-	id = g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE,
-	                       G_CALLBACK (connection_visibility_changed),
-	                       self);
-	g_object_set_data (G_OBJECT (connection), VISIBLE_ID_TAG, GUINT_TO_POINTER (id));
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
+	                  G_CALLBACK (connection_removed), self);
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED,
+	                  G_CALLBACK (connection_updated), self);
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
+	                  G_CALLBACK (connection_updated_by_user), self);
+	g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE,
+	                  G_CALLBACK (connection_visibility_changed),
+	                  self);
 
 	/* Export the connection over D-Bus */
 	g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
 	path = g_strdup_printf ("%s/%u", NM_DBUS_PATH_SETTINGS, ec_counter++);
 	nm_connection_set_path (NM_CONNECTION (connection), path);
-	dbus_g_connection_register_g_object (priv->bus, path, G_OBJECT (connection));
+	nm_dbus_manager_register_object (priv->dbus_mgr, path, G_OBJECT (connection));
 	g_free (path);
 
 	g_hash_table_insert (priv->connections,
@@ -871,39 +875,38 @@ claim_connection (NMSettings *self,
 	                     g_object_ref (connection));
 
 	/* Only emit the individual connection-added signal after connections
-	 * have been initially loaded.  While getting the first list of connections
-	 * we suppress it, then send the connections-loaded signal after we're all
-	 * done to minimize processing.
+	 * have been initially loaded.
 	 */
 	if (priv->connections_loaded) {
 		/* Internal added signal */
 		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
 		g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_ADDED, connection);
+		g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
 
 		/* Exported D-Bus signal */
 		g_signal_emit (self, signals[NEW_CONNECTION], 0, connection);
 	}
 }
 
-static void
-remove_default_wired_connection (NMSettings *self,
-                                 NMSettingsConnection *connection,
-                                 gboolean do_signal)
-{
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	const char *path = nm_connection_get_path (NM_CONNECTION (connection));
-
-	if (g_hash_table_lookup (priv->connections, path)) {
-		if (do_signal)
-			g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_REMOVED);
-		g_hash_table_remove (priv->connections, path);
-	}
-}
-
-static NMSettingsConnection *
-add_new_connection (NMSettings *self,
-                    NMConnection *connection,
-                    GError **error)
+/**
+ * nm_settings_add_connection:
+ * @self: the #NMSettings object
+ * @connection: the source connection to create a new #NMSettingsConnection from
+ * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
+ * not save to disk
+ * @error: on return, a location to store any errors that may occur
+ *
+ * Creates a new #NMSettingsConnection for the given source @connection.  
+ * The returned object is owned by @self and the caller must reference
+ * the object to continue using it.
+ *
+ * Returns: the new #NMSettingsConnection or %NULL
+ */
+NMSettingsConnection *
+nm_settings_add_connection (NMSettings *self,
+                            NMConnection *connection,
+                            gboolean save_to_disk,
+                            GError **error)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
@@ -936,24 +939,31 @@ add_new_connection (NMSettings *self,
 		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
 		GError *add_error = NULL;
 
-		g_clear_error (error);
-		added = nm_system_config_interface_add_connection (plugin, connection, &add_error);
+		added = nm_system_config_interface_add_connection (plugin, connection, save_to_disk, &add_error);
 		if (added) {
 			claim_connection (self, added, TRUE);
 			return added;
 		}
-		g_propagate_error (error, add_error);
+		nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s",
+		            nm_connection_get_uuid (connection),
+		            nm_connection_get_id (connection),
+		            add_error ? add_error->message : "(unknown)");
+		g_clear_error (&add_error);
 	}
+
+	g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_FAILED,
+	                     "No plugin supported adding this connection");
 	return NULL;
 }
 
 static NMConnection *
 _nm_connection_provider_add_connection (NMConnectionProvider *provider,
                                         NMConnection *connection,
+                                        gboolean save_to_disk,
                                         GError **error)
 {
 	g_assert (NM_IS_CONNECTION_PROVIDER (provider) && NM_IS_SETTINGS (provider));
-	return (NMConnection *) add_new_connection (NM_SETTINGS (provider), connection, error);
+	return NM_CONNECTION (nm_settings_add_connection (NM_SETTINGS (provider), connection, save_to_disk, error));
 }
 
 static gboolean
@@ -978,7 +988,7 @@ secrets_filter_cb (NMSetting *setting,
 static void
 send_agent_owned_secrets (NMSettings *self,
                           NMSettingsConnection *connection,
-                          gulong caller_uid)
+                          NMAuthSubject *subject)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	NMConnection *for_agent;
@@ -991,7 +1001,7 @@ send_agent_owned_secrets (NMSettings *self,
 	nm_connection_clear_secrets_with_flags (for_agent,
 	                                        secrets_filter_cb,
 	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
-	nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, TRUE, caller_uid);
+	nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, subject);
 	g_object_unref (for_agent);
 }
 
@@ -1004,76 +1014,54 @@ pk_add_cb (NMAuthChain *chain,
 	NMSettings *self = NM_SETTINGS (user_data);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	NMAuthCallResult result;
-	GError *error = NULL, *add_error = NULL;
+	GError *error = NULL;
 	NMConnection *connection;
 	NMSettingsConnection *added = NULL;
 	NMSettingsAddCallback callback;
 	gpointer callback_data;
-	gulong caller_uid;
+	NMAuthSubject *subject;
 	const char *perm;
+	gboolean save_to_disk;
+
+	g_assert (context);
 
 	priv->auths = g_slist_remove (priv->auths, chain);
 
+	perm = nm_auth_chain_get_data (chain, "perm");
+	g_assert (perm);
+	result = nm_auth_chain_get_result (chain, perm);
+
 	if (chain_error) {
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_GENERAL,
 		                     "Error checking authorization: %s",
 		                     chain_error->message ? chain_error->message : "(unknown)");
-		goto done;
-	}
-
-	perm = nm_auth_chain_get_data (chain, "perm");
-	g_assert (perm);
-	result = nm_auth_chain_get_result (chain, perm);
-
-	/* Caller didn't successfully authenticate */
-	if (result != NM_AUTH_CALL_RESULT_YES) {
+	} else if (result != NM_AUTH_CALL_RESULT_YES) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
 		                             "Insufficient privileges.");
-		goto done;
-	}
-
-	connection = nm_auth_chain_get_data (chain, "connection");
-	g_assert (connection);
-	added = add_new_connection (self, connection, &add_error);
-	if (!added) {
-		error = g_error_new (NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_ADD_FAILED,
-		                     "Saving connection failed: (%d) %s",
-		                     add_error ? add_error->code : -1,
-		                     (add_error && add_error->message) ? add_error->message : "(unknown)");
-		g_error_free (add_error);
+	} else {
+		/* Authorized */
+		connection = nm_auth_chain_get_data (chain, "connection");
+		g_assert (connection);
+		save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk"));
+		added = nm_settings_add_connection (self, connection, save_to_disk, &error);
 	}
 
-done:
 	callback = nm_auth_chain_get_data (chain, "callback");
 	callback_data = nm_auth_chain_get_data (chain, "callback-data");
-	caller_uid = nm_auth_chain_get_data_ulong (chain, "caller-uid");
+	subject = nm_auth_chain_get_data (chain, "subject");
 
 	callback (self, added, error, context, callback_data);
 
 	/* Send agent-owned secrets to the agents */
 	if (!error && added)
-		send_agent_owned_secrets (self, added, caller_uid);
+		send_agent_owned_secrets (self, added, subject);
 
 	g_clear_error (&error);
 	nm_auth_chain_unref (chain);
 }
 
-static void
-add_cb (NMSettings *self,
-        NMSettingsConnection *connection,
-        GError *error,
-        DBusGMethodInvocation *context,
-        gpointer user_data)
-{
-	if (error)
-		dbus_g_method_return_error (context, error);
-	else
-		dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection)));
-}
-
 /* FIXME: remove if/when kernel supports adhoc wpa */
 static gboolean
 is_adhoc_wpa (NMConnection *connection)
@@ -1107,20 +1095,24 @@ is_adhoc_wpa (NMConnection *connection)
 }
 
 void
-nm_settings_add_connection (NMSettings *self,
+nm_settings_add_connection_dbus (NMSettings *self,
                             NMConnection *connection,
+                            gboolean save_to_disk,
                             DBusGMethodInvocation *context,
                             NMSettingsAddCallback callback,
                             gpointer user_data)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
+	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
 	GError *error = NULL, *tmp_error = NULL;
-	gulong caller_uid = G_MAXULONG;
 	char *error_desc = NULL;
 	const char *perm;
 
+	g_return_if_fail (connection != NULL);
+	g_return_if_fail (context != NULL);
+
 	/* Connection must be valid, of course */
 	if (!nm_connection_verify (connection, &tmp_error)) {
 		error = g_error_new (NM_SETTINGS_ERROR,
@@ -1128,9 +1120,7 @@ nm_settings_add_connection (NMSettings *self,
 		                     "The connection was invalid: %s",
 		                     tmp_error ? tmp_error->message : "(unknown)");
 		g_error_free (tmp_error);
-		callback (self, NULL, error, context, user_data);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
 	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
@@ -1141,9 +1131,7 @@ nm_settings_add_connection (NMSettings *self,
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                             "WPA Ad-Hoc disabled due to kernel bugs");
-		callback (self, NULL, error, context, user_data);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
 	/* Do any of the plugins support adding? */
@@ -1151,34 +1139,29 @@ nm_settings_add_connection (NMSettings *self,
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_ADD_NOT_SUPPORTED,
 		                             "None of the registered plugins support add.");
-		callback (self, NULL, error, context, user_data);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
-	/* Get the caller's UID */
-	if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &caller_uid, &error_desc)) {
-		error = g_error_new (NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_NOT_PRIVILEGED,
-		                     "Unable to determine UID of request: %s.",
-		                     error_desc ? error_desc : "(unknown)");
-		g_free (error_desc);
-		callback (self, NULL, error, context, user_data);
-		g_error_free (error);
-		return;
+	subject = nm_auth_subject_new_from_context (context);
+	if (!subject) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             "Unable to determine UID of request.");
+		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_uid_in_acl (connection, priv->session_monitor, caller_uid, &error_desc)) {
+	if (!nm_auth_uid_in_acl (connection,
+	                         nm_session_monitor_get (),
+	                         nm_auth_subject_get_uid (subject),
+	                         &error_desc)) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
 		                             error_desc);
 		g_free (error_desc);
-		callback (self, NULL, error, context, user_data);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
 	/* If the caller is the only user in the connection's permissions, then
@@ -1192,29 +1175,62 @@ nm_settings_add_connection (NMSettings *self,
 	else
 		perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
 
-	/* Otherwise validate the user request */
-	chain = nm_auth_chain_new (context, NULL, pk_add_cb, self);
-	g_assert (chain);
+	/* Validate the user request */
+	chain = nm_auth_chain_new_subject (subject, context, pk_add_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate the request.");
+		goto done;
+	}
+
 	priv->auths = g_slist_append (priv->auths, chain);
 	nm_auth_chain_add_call (chain, perm, TRUE);
 	nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL);
 	nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref);
 	nm_auth_chain_set_data (chain, "callback", callback, NULL);
 	nm_auth_chain_set_data (chain, "callback-data", user_data, NULL);
-	nm_auth_chain_set_data_ulong (chain, "caller-uid", caller_uid);
+	nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref);
+	nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL);
+
+done:
+	if (error)
+		callback (self, NULL, error, context, user_data);
+
+	g_clear_error (&error);
+	g_clear_object (&subject);
 }
 
 static void
-impl_settings_add_connection (NMSettings *self,
-                              GHashTable *settings,
-                              DBusGMethodInvocation *context)
+impl_settings_add_connection_add_cb (NMSettings *self,
+                                     NMSettingsConnection *connection,
+                                     GError *error,
+                                     DBusGMethodInvocation *context,
+                                     gpointer user_data)
+{
+	if (error)
+		dbus_g_method_return_error (context, error);
+	else
+		dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection)));
+}
+
+static void
+impl_settings_add_connection_helper (NMSettings *self,
+                                     GHashTable *settings,
+                                     gboolean save_to_disk,
+                                     DBusGMethodInvocation *context)
 {
 	NMConnection *connection;
 	GError *error = NULL;
 
 	connection = nm_connection_new_from_hash (settings, &error);
 	if (connection) {
-		nm_settings_add_connection (self, connection, context, add_cb, NULL);
+		nm_settings_add_connection_dbus (self,
+		                            connection,
+		                            save_to_disk,
+		                            context,
+		                            impl_settings_add_connection_add_cb,
+		                            NULL);
 		g_object_unref (connection);
 	} else {
 		g_assert (error);
@@ -1224,6 +1240,103 @@ impl_settings_add_connection (NMSettings *self,
 }
 
 static void
+impl_settings_add_connection (NMSettings *self,
+                              GHashTable *settings,
+                              DBusGMethodInvocation *context)
+{
+	impl_settings_add_connection_helper (self, settings, TRUE, context);
+}
+
+static void
+impl_settings_add_connection_unsaved (NMSettings *self,
+                                      GHashTable *settings,
+                                      DBusGMethodInvocation *context)
+{
+	impl_settings_add_connection_helper (self, settings, FALSE, context);
+}
+
+static gboolean
+ensure_root (NMDBusManager         *dbus_mgr,
+             DBusGMethodInvocation *context)
+{
+	gulong caller_uid;
+	GError *error = NULL;
+
+	if (!nm_dbus_manager_get_caller_info (dbus_mgr, context, NULL, &caller_uid, NULL)) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             "Unable to determine request UID.");
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+		return FALSE;
+	}
+	if (caller_uid != 0) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             "Permission denied");
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+impl_settings_load_connections (NMSettings *self,
+                                char **filenames,
+                                DBusGMethodInvocation *context)
+{
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GPtrArray *failures;
+	GSList *iter;
+	int i;
+
+	if (!ensure_root (priv->dbus_mgr, context))
+		return;
+
+	failures = g_ptr_array_new ();
+
+	for (i = 0; filenames[i]; i++) {
+		for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+			NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
+
+			if (nm_system_config_interface_load_connection (plugin, filenames[i]))
+				break;
+		}
+
+		if (!iter) {
+			if (!g_path_is_absolute (filenames[i]))
+				nm_log_warn (LOGD_SETTINGS, "Connection filename '%s' is not an absolute path", filenames[i]);
+			g_ptr_array_add (failures, (char *) filenames[i]);
+		}
+	}
+
+	g_ptr_array_add (failures, NULL);
+	dbus_g_method_return (context, failures->len == 1, failures->pdata);
+	g_ptr_array_unref (failures);
+}
+
+static void
+impl_settings_reload_connections (NMSettings *self,
+                                  DBusGMethodInvocation *context)
+{
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (!ensure_root (priv->dbus_mgr, context))
+		return;
+
+	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
+
+		nm_system_config_interface_reload_connections (plugin);
+	}
+
+	dbus_g_method_return (context, TRUE);
+}
+
+static void
 pk_hostname_cb (NMAuthChain *chain,
                 GError *chain_error,
                 DBusGMethodInvocation *context,
@@ -1232,51 +1345,45 @@ pk_hostname_cb (NMAuthChain *chain,
 	NMSettings *self = NM_SETTINGS (user_data);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	NMAuthCallResult result;
-	gboolean success = FALSE;
 	GError *error = NULL;
 	GSList *iter;
 	const char *hostname;
 
+	g_assert (context);
+
 	priv->auths = g_slist_remove (priv->auths, chain);
 
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
+
 	/* If our NMSettingsConnection is already gone, do nothing */
 	if (chain_error) {
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_GENERAL,
 		                     "Error checking authorization: %s",
 		                     chain_error->message ? chain_error->message : "(unknown)");
-		goto done;
-	}
-
-	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
-
-	/* Caller didn't successfully authenticate */
-	if (result != NM_AUTH_CALL_RESULT_YES) {
+	} else if (result != NM_AUTH_CALL_RESULT_YES) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
 		                             "Insufficient privileges.");
-		goto done;
-	}
-
-	/* Set the hostname in all plugins */
-	hostname = nm_auth_chain_get_data (chain, "hostname");
-	for (iter = priv->plugins; iter; iter = iter->next) {
-		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
-
-		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
-		if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
-			g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, hostname, NULL);
-			success = TRUE;
+	} else {
+		/* Set the hostname in all plugins */
+		hostname = nm_auth_chain_get_data (chain, "hostname");
+		for (iter = priv->plugins; iter; iter = iter->next) {
+			NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
+
+			/* error will be cleared if any plugin supports saving the hostname */
+			error = g_error_new_literal (NM_SETTINGS_ERROR,
+			                             NM_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED,
+			                             "Saving the hostname failed.");
+
+			g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
+			if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
+				g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, hostname, NULL);
+				g_clear_error (&error);
+			}
 		}
 	}
 
-	if (!success) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED,
-		                             "Saving the hostname failed.");
-	}
-
-done:
 	if (error)
 		dbus_g_method_return_error (context, error);
 	else
@@ -1327,9 +1434,7 @@ impl_settings_save_hostname (NMSettings *self,
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_HOSTNAME_INVALID,
 		                             "The hostname was too long or contained invalid characters.");
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
 	/* Do any of the plugins support setting the hostname? */
@@ -1337,33 +1442,42 @@ impl_settings_save_hostname (NMSettings *self,
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED,
 		                             "None of the registered plugins support setting the hostname.");
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		return;
+		goto done;
+	}
+
+	chain = nm_auth_chain_new_context (context, 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;
 	}
 
-	/* Otherwise validate the user request */
-	chain = nm_auth_chain_new (context, NULL, pk_hostname_cb, self);
-	g_assert (chain);
 	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)
+		dbus_g_method_return_error (context, error);
+	g_clear_error (&error);
 }
 
 static gboolean
-have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device)
+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;
-	NMSettingInfiniband *s_infiniband;
 	const GByteArray *setting_mac;
-	gboolean ret = FALSE;
+	const guint8 *hwaddr;
+	guint hwaddr_len = 0;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
-	g_return_val_if_fail (mac != NULL, FALSE);
+
+	hwaddr = nm_device_get_hw_address (device, &hwaddr_len);
 
 	/* Find a wired connection locked to the given MAC address, if any */
 	g_hash_table_iter_init (&iter, priv->connections);
@@ -1372,344 +1486,221 @@ have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device)
 		const char *ctype, *iface;
 
 		s_con = nm_connection_get_setting_connection (connection);
-		ctype = nm_setting_connection_get_connection_type (s_con);
 
-		iface = nm_connection_get_virtual_iface_name (connection);
-		if (iface) {
-			if (!strcmp (iface, nm_device_get_iface (device))) {
-				ret = TRUE;
-				break;
-			} else
-				continue;
-		}
+		iface = nm_setting_connection_get_interface_name (s_con);
+		if (iface && strcmp (iface, nm_device_get_iface (device)) != 0)
+			continue;
 
+		ctype = nm_setting_connection_get_connection_type (s_con);
 		if (   strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME)
-		    && strcmp (ctype, NM_SETTING_INFINIBAND_SETTING_NAME)
 		    && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
 			continue;
 
 		s_wired = nm_connection_get_setting_wired (connection);
-		s_infiniband = nm_connection_get_setting_infiniband (connection);
 
-		/* No wired setting; therefore the PPPoE connection applies to any device */
 		if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
-			ret = TRUE;
-			break;
+			/* No wired setting; therefore the PPPoE connection applies to any device */
+			return TRUE;
 		}
 
-		g_assert (s_wired != NULL || s_infiniband != NULL);
+		g_assert (s_wired != NULL);
 
-		setting_mac = s_wired ?
-			nm_setting_wired_get_mac_address (s_wired) :
-			nm_setting_infiniband_get_mac_address (s_infiniband);
+		setting_mac = nm_setting_wired_get_mac_address (s_wired);
 		if (setting_mac) {
 			/* A connection mac-locked to this device */
-			if (mac->len == setting_mac->len &&
-				!memcmp (setting_mac->data, mac->data, mac->len)) {
-				ret = TRUE;
-				break;
-			}
+			if (hwaddr_len == setting_mac->len &&
+				!memcmp (setting_mac->data, hwaddr, hwaddr_len))
+				return TRUE;
 		} else {
 			/* A connection that applies to any wired device */
-			ret = TRUE;
-			break;
-		}
-	}
-
-	return ret;
-}
-
-/* Search through the list of blacklisted MAC addresses in the config file. */
-static gboolean
-is_mac_auto_wired_blacklisted (NMSettings *self, const GByteArray *mac)
-{
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GKeyFile *config;
-	char **list, **iter;
-	gboolean found = FALSE;
-	int hwaddr_type;
-
-	g_return_val_if_fail (mac != NULL, FALSE);
-
-	if (!priv->config_file)
-		return FALSE;
-
-	config = g_key_file_new ();
-	if (!config) {
-		nm_log_warn (LOGD_SETTINGS, "not enough memory to load config file.");
-		return FALSE;
-	}
-
-	g_key_file_set_list_separator (config, ',');
-	if (!g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_NONE, NULL))
-		goto out;
-
-	hwaddr_type = nm_utils_hwaddr_type (mac->len);
-
-	list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, NULL, NULL);
-	for (iter = list; iter && *iter; iter++) {
-		guint8 *candidate, buffer[NM_UTILS_HWADDR_LEN_MAX];
-
-		if (strcmp (g_strstrip (*iter), "*") == 0) {
-			found = TRUE;
-			break;
-		}
-
-		candidate = nm_utils_hwaddr_aton (*iter, hwaddr_type, buffer);
-		if (candidate && !memcmp (mac->data, candidate, mac->len)) {
-			found = TRUE;
-			break;
+			return TRUE;
 		}
 	}
 
-	if (list)
-		g_strfreev (list);
+	/* See if there's a known non-NetworkManager configuration for the device */
+	if (nm_device_spec_match_list (device, priv->unrecognized_specs))
+		return TRUE;
 
-out:
-	g_key_file_free (config);
-	return found;
+	return FALSE;
 }
 
-#define DEFAULT_WIRED_TAG "default-wired"
+#define DEFAULT_WIRED_CONNECTION_TAG "default-wired-connection"
+#define DEFAULT_WIRED_DEVICE_TAG     "default-wired-device"
+
+static void default_wired_clear_tag (NMSettings *self,
+                                     NMDevice *device,
+                                     NMSettingsConnection *connection,
+                                     gboolean add_to_no_auto_default);
 
 static void
-default_wired_deleted (NMDefaultWiredConnection *wired,
-                       const GByteArray *mac,
-                       NMSettings *self)
+default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	NMSettingConnection *s_con;
-	int hwaddr_type;
-	char *tmp;
-	GKeyFile *config;
-	char **list, **iter, **updated;
-	gboolean found = FALSE;
-	gsize len = 0, i;
-	char *data;
-
-	/* If there was no config file specified, there's nothing to do */
-	if (!priv->config_file)
-		goto cleanup;
-
-	/* 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.
-	 */
+	NMDevice *device;
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (wired));
-	g_assert (s_con);
-
-	/* Ignore removals of read-only connections, since they couldn't have
-	 * been removed by the user.
+	/* 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.
 	 */
-	if (nm_setting_connection_get_read_only (s_con))
-		goto cleanup;
-
-	config = g_key_file_new ();
-	if (!config)
-		goto cleanup;
-
-	if (nm_connection_get_setting_wired (NM_CONNECTION (wired)))
-		hwaddr_type = ARPHRD_ETHER;
-	else if (nm_connection_get_setting_infiniband (NM_CONNECTION (wired)))
-		hwaddr_type = ARPHRD_INFINIBAND;
-	else
-		goto cleanup;
-
-	g_key_file_set_list_separator (config, ',');
-	g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_KEEP_COMMENTS, NULL);
-
-	list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL);
-	for (iter = list; iter && *iter; iter++) {
-		guint8 *candidate, buffer[NM_UTILS_HWADDR_LEN_MAX];
-
-		if (strcmp (g_strstrip (*iter), "*") == 0) {
-			found = TRUE;
-			break;
-		}
-
-		candidate = nm_utils_hwaddr_aton (*iter, hwaddr_type, buffer);
-		if (candidate && !memcmp (mac->data, candidate, mac->len)) {
-			found = TRUE;
-			break;
-		}
-	}
-
-	/* Add this device's MAC to the list */
-	if (!found) {
-		tmp = nm_utils_hwaddr_ntoa (mac->data, hwaddr_type);
-
-		/* New list; size + 1 for the new element, + 1 again for ending NULL */
-		updated = g_malloc0 (sizeof (char*) * (len + 2));
-
-		/* Copy original list and add new MAC */
-		for (i = 0; list && list[i]; i++)
-			updated[i] = list[i];
-		updated[i++] = tmp;
-		updated[i] = NULL;
-
-		g_key_file_set_string_list (config,
-		                            "main", CONFIG_KEY_NO_AUTO_DEFAULT,
-		                            (const char **) updated,
-		                            len + 2);
-		/* g_free() not g_strfreev() since 'updated' isn't a deep-copy */
-		g_free (updated);
-		g_free (tmp);
-
-		data = g_key_file_to_data (config, &len, NULL);
-		if (data) {
-			g_file_set_contents (priv->config_file, data, len, NULL);
-			g_free (data);
-		}
-	}
-
-	if (list)
-		g_strfreev (list);
-	g_key_file_free (config);
-
-cleanup:
-	g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)),
-	                   DEFAULT_WIRED_TAG,
-	                   NULL);
+	device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
+	if (device)
+		default_wired_clear_tag (self, device, connection, TRUE);
 }
 
 static void
-delete_cb (NMSettingsConnection *connection, GError *error, gpointer user_data)
+default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, NMSettings *self)
 {
+	NMDevice *device;
+
+	/* The connection has been changed by the user, it should no longer be
+	 * considered a default wired connection, and should no longer affect
+	 * the no-auto-default configuration option.
+	 */
+	device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
+	if (device)
+		default_wired_clear_tag (self, device, connection, FALSE);
 }
 
 static void
-default_wired_try_update (NMDefaultWiredConnection *wired,
-                          NMSettings *self)
+default_wired_clear_tag (NMSettings *self,
+                         NMDevice *device,
+                         NMSettingsConnection *connection,
+                         gboolean add_to_no_auto_default)
 {
-	GError *error = NULL;
-	const char *id;
-	NMSettingsConnection *added;
-
-	/* Try to move this default wired conneciton to a plugin so that it has
-	 * persistent storage.
-	 */
-
-	/* Keep it alive over removal so we can re-add it if we need to */
-	g_object_ref (wired);
+	g_return_if_fail (NM_IS_SETTINGS (self));
+	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+	g_return_if_fail (device == g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG));
+	g_return_if_fail (connection == g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG));
 
-	id = nm_connection_get_id (NM_CONNECTION (wired));
-	g_assert (id);
+	g_object_set_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG, NULL);
+	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, NULL);
 
-	remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE);
-	added = add_new_connection (self, NM_CONNECTION (wired), &error);
-	if (added) {
-		nm_settings_connection_delete (NM_SETTINGS_CONNECTION (wired), delete_cb, 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);
 
-		g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)),
-		                   DEFAULT_WIRED_TAG,
-		                   NULL);
-		nm_log_info (LOGD_SETTINGS, "Saved default wired connection '%s' to persistent storage", id);
-	} else {
-		nm_log_warn (LOGD_SETTINGS, "couldn't save default wired connection '%s': %d / %s",
-			         id,
-			         error ? error->code : -1,
-			         (error && error->message) ? error->message : "(unknown)");
-		g_clear_error (&error);
-
-		/* If there was an error, don't destroy the default wired connection,
-		 * but add it back to the system settings service. Connection is already
-		 * exported on the bus, don't export it again, thus do_export == FALSE.
-		 */
-		claim_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE);
-	}
-
-	g_object_unref (wired);
+	if (add_to_no_auto_default)
+		nm_config_set_ethernet_no_auto_default (NM_SETTINGS_GET_PRIVATE (self)->config, NM_CONFIG_DEVICE (device));
 }
 
 void
 nm_settings_device_added (NMSettings *self, NMDevice *device)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GByteArray *mac = NULL;
-	const guint8 *hwaddr;
-	guint hwaddr_len = 0;
-	NMDefaultWiredConnection *wired;
-	gboolean read_only = TRUE;
-	const char *id;
-	char *defname;
+	NMConnection *connection;
+	NMSettingsConnection *added;
+	NMSetting *setting;
+	GError *error = NULL;
+	const guint8 *hw_address;
+	char *defname, *uuid;
+	guint len = 0;
+	GByteArray *mac;
 
-	if (!NM_IS_DEVICE_WIRED (device))
+	if (!NM_IS_DEVICE_ETHERNET (device))
 		return;
 
 	/* If the device isn't managed or it already has a default wired connection,
 	 * ignore it.
 	 */
 	if (   !nm_device_get_managed (device)
-	    || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG))
+	    || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG)
+	    || have_connection_for_device (self, device)
+	    || !nm_config_get_ethernet_can_auto_default (priv->config, NM_CONFIG_DEVICE (device)))
 		return;
 
-	hwaddr = nm_device_get_hw_address (device, &hwaddr_len);
-
-	mac = g_byte_array_sized_new (hwaddr_len);
-	g_byte_array_append (mac, hwaddr, hwaddr_len);
-
-	if (   have_connection_for_device (self, mac, device)
-		|| is_mac_auto_wired_blacklisted (self, mac))
-		goto ignore;
+	hw_address = nm_device_get_hw_address (device, &len);
+	if (!hw_address)
+		return;
 
-	if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS))
-		read_only = FALSE;
+	connection = nm_connection_new ();
+	g_assert (connection);
+	setting = nm_setting_connection_new ();
+	g_assert (setting);
+	nm_connection_add_setting (connection, setting);
 
 	defname = nm_settings_utils_get_default_wired_name (priv->connections);
-	wired = nm_default_wired_connection_new (mac, device, defname, read_only);
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, defname,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
+	              NULL);
+	g_free (uuid);
 	g_free (defname);
-	if (!wired)
-		goto ignore;
 
-	id = nm_connection_get_id (NM_CONNECTION (wired));
-	g_assert (id);
+	/* Lock the connection to the device */
+	setting = nm_setting_wired_new ();
+	nm_connection_add_setting (connection, setting);
+
+	mac = g_byte_array_sized_new (len);
+	g_byte_array_append (mac, hw_address, len);
+	g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
+	g_byte_array_unref (mac);
+
+	/* Add the connection */
+	added = nm_settings_add_connection (self, connection, FALSE, &error);
+	g_object_unref (connection);
 
-	nm_log_info (LOGD_SETTINGS, "Added default wired connection '%s' for %s",
-	             id, nm_device_get_udi (device));
+	if (!added) {
+		nm_log_warn (LOGD_SETTINGS, "(%s) couldn't create default wired connection: %s",
+		             nm_device_get_iface (device),
+		             (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return;
+	}
 
-	g_signal_connect (wired, "try-update", (GCallback) default_wired_try_update, self);
-	g_signal_connect (wired, "deleted", (GCallback) default_wired_deleted, self);
-	claim_connection (self, NM_SETTINGS_CONNECTION (wired), TRUE);
-	g_object_unref (wired);
+	g_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device);
+	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added);
 
-	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_TAG, wired);
+	g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
+	                  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);
 
-ignore:
-	g_byte_array_free (mac, TRUE);
+	nm_log_info (LOGD_SETTINGS, "(%s): created default wired connection '%s'",
+	             nm_device_get_iface (device),
+	             nm_connection_get_id (NM_CONNECTION (added)));
 }
 
 void
-nm_settings_device_removed (NMSettings *self, NMDevice *device)
+nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting)
 {
-	NMDefaultWiredConnection *connection;
+	NMSettingsConnection *connection;
 
-	if (!NM_IS_DEVICE_WIRED (device))
-		return;
+	connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG);
+	if (connection) {
+		default_wired_clear_tag (self, device, connection, FALSE);
 
-	connection = (NMDefaultWiredConnection *) g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG);
-	if (connection)
-		remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (connection), TRUE);
+		/* Don't delete the default wired connection on shutdown, so that it
+		 * remains up and can be assumed if NM starts again.
+		 */
+		if (quitting == FALSE)
+			nm_settings_connection_delete (connection, NULL, NULL);
+	}
 }
 
 /***************************************************************/
 
-static gint
-best_connection_sort (gconstpointer a, gconstpointer b, gpointer user_data)
+/* GCompareFunc helper for sorting "best" connections.
+ * The function sorts connections in ascending timestamp order.
+ * That means an older connection (lower timestamp) goes before
+ * a newer one.
+ */
+gint
+nm_settings_sort_connections (gconstpointer a, gconstpointer b)
 {
 	NMSettingsConnection *ac = (NMSettingsConnection *) a;
 	NMSettingsConnection *bc = (NMSettingsConnection *) b;
 	guint64 ats = 0, bts = 0;
 
-	if (!ac && bc)
+	if (ac == bc)
+		return 0;
+	if (!ac)
 		return -1;
-	else if (ac && !bc)
+	if (!bc)
 		return 1;
-	else if (!ac && !bc)
-		return 0;
-
-	g_assert (ac && bc);
 
 	/* In the future we may use connection priorities in addition to timestamps */
 	nm_settings_connection_get_timestamp (ac, &ats);
@@ -1757,7 +1748,7 @@ get_best_connections (NMConnectionProvider *provider,
 		}
 
 		/* List is sorted with oldest first */
-		sorted = g_slist_insert_sorted_with_data (sorted, connection, best_connection_sort, NULL);
+		sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
 		added++;
 
 		if (max_requested && added > max_requested) {
@@ -1775,58 +1766,53 @@ get_best_connections (NMConnectionProvider *provider,
 static const GSList *
 get_connections (NMConnectionProvider *provider)
 {
-	static GSList *list = NULL;
+	GSList *list = NULL;
 	NMSettings *self = NM_SETTINGS (provider);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GHashTableIter iter;
 	NMSettingsConnection *connection;
 
-	/* Lazily free the list with every call so we can keep it 'const' for callers */
-	g_slist_free (list);
-	list = NULL;
-
-	g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections);
+	g_hash_table_iter_init (&iter, priv->connections);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection))
 		list = g_slist_prepend (list, connection);
-	return g_slist_reverse (list);
+	list = g_slist_reverse (list);
+
+	/* Cache the list every call so we can keep it 'const' for callers */
+	g_slist_free (priv->get_connections_cache);
+	priv->get_connections_cache = list;
+	return list;
 }
 
-static gboolean
-has_connections_loaded (NMConnectionProvider *provider)
+static NMConnection *
+cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (provider);
-
-	return priv->connections_loaded;
+	return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid));
 }
 
 /***************************************************************/
 
 NMSettings *
-nm_settings_new (const char *config_file,
-                 const char **plugins,
-                 GError **error)
+nm_settings_new (GError **error)
 {
 	NMSettings *self;
 	NMSettingsPrivate *priv;
 
 	self = g_object_new (NM_TYPE_SETTINGS, NULL);
-	if (!self)
-		return NULL;
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	priv->config_file = g_strdup (config_file);
+	priv->config = nm_config_get ();
 	priv->dbus_mgr = nm_dbus_manager_get ();
-	priv->bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
 
 	/* Load the plugins; fail if a plugin is not found. */
-	if (!load_plugins (self, plugins, error)) {
+	if (!load_plugins (self, nm_config_get_plugins (priv->config), error)) {
 		g_object_unref (self);
 		return NULL;
 	}
 
-	unmanaged_specs_changed (NULL, self);
+	load_connections (self);
 
-	dbus_g_connection_register_g_object (priv->bus, NM_DBUS_PATH_SETTINGS, G_OBJECT (self));
+	nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH_SETTINGS, self);
 	return self;
 }
 
@@ -1835,8 +1821,8 @@ connection_provider_init (NMConnectionProvider *cp_class)
 {
     cp_class->get_best_connections = get_best_connections;
     cp_class->get_connections = get_connections;
-    cp_class->has_connections_loaded = has_connections_loaded;
     cp_class->add_connection = _nm_connection_provider_add_connection;
+    cp_class->get_connection_by_uuid = cp_get_connection_by_uuid;
 }
 
 static void
@@ -1846,8 +1832,6 @@ nm_settings_init (NMSettings *self)
 
 	priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
 
-	priv->session_monitor = nm_session_monitor_get ();
-
 	/* Hold a reference to the agent manager so it stays alive; the only
 	 * other holders are NMSettingsConnection objects which are often
 	 * transient, and we don't want the agent manager to get destroyed and
@@ -1863,15 +1847,12 @@ dispose (GObject *object)
 {
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GSList *iter;
 
-	for (iter = priv->auths; iter; iter = g_slist_next (iter))
-		nm_auth_chain_unref ((NMAuthChain *) iter->data);
-	g_slist_free (priv->auths);
+	g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref);
+	priv->auths = NULL;
 
-	g_object_unref (priv->dbus_mgr);
+	priv->dbus_mgr = NULL;
 
-	g_object_unref (priv->session_monitor);
 	g_object_unref (priv->agent_mgr);
 
 	G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object);
@@ -1884,13 +1865,12 @@ finalize (GObject *object)
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
 	g_hash_table_destroy (priv->connections);
+	g_slist_free (priv->get_connections_cache);
 
-	clear_unmanaged_specs (self);
-
-	g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL);
-	g_slist_free (priv->plugins);
+	g_slist_free_full (priv->unmanaged_specs, g_free);
+	g_slist_free_full (priv->unrecognized_specs, g_free);
 
-	g_free (priv->config_file);
+	g_slist_free_full (priv->plugins, g_object_unref);
 
 	G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
 }
@@ -1900,8 +1880,12 @@ get_property (GObject *object, guint prop_id,
 			  GValue *value, GParamSpec *pspec)
 {
 	NMSettings *self = NM_SETTINGS (object);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	const GSList *specs, *iter;
 	GSList *copy = NULL;
+	GHashTableIter citer;
+	GPtrArray *array;
+	const char *path;
 
 	switch (prop_id) {
 	case PROP_UNMANAGED_SPECS:
@@ -1920,6 +1904,13 @@ get_property (GObject *object, guint prop_id,
 	case PROP_CAN_MODIFY:
 		g_value_set_boolean (value, !!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS));
 		break;
+	case PROP_CONNECTIONS:
+		array = g_ptr_array_sized_new (g_hash_table_size (priv->connections));
+		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_value_take_boxed (value, array);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1965,6 +1956,14 @@ nm_settings_class_init (NMSettingsClass *class)
 		                       FALSE,
 		                       G_PARAM_READABLE));
 
+	g_object_class_install_property
+		(object_class, PROP_CONNECTIONS,
+		 g_param_spec_boxed (NM_SETTINGS_CONNECTIONS,
+		                     "Connections",
+		                     "Connections",
+		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
 	/* signals */
 	signals[PROPERTIES_CHANGED] = 
 	                g_signal_new ("properties-changed",
@@ -1992,6 +1991,15 @@ nm_settings_class_init (NMSettingsClass *class)
 	                              g_cclosure_marshal_VOID__OBJECT,
 	                              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
+	signals[CONNECTION_UPDATED_BY_USER] =
+	                g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER,
+	                              G_OBJECT_CLASS_TYPE (object_class),
+	                              G_SIGNAL_RUN_FIRST,
+	                              0,
+	                              NULL, NULL,
+	                              g_cclosure_marshal_VOID__OBJECT,
+	                              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
 	signals[CONNECTION_REMOVED] = 
 	                g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
 	                              G_OBJECT_CLASS_TYPE (object_class),
@@ -2010,15 +2018,6 @@ nm_settings_class_init (NMSettingsClass *class)
 	                              g_cclosure_marshal_VOID__OBJECT,
 	                              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
-	signals[CONNECTIONS_LOADED] = 
-	                g_signal_new (NM_SETTINGS_SIGNAL_CONNECTIONS_LOADED,
-	                              G_OBJECT_CLASS_TYPE (object_class),
-	                              G_SIGNAL_RUN_FIRST,
-	                              G_STRUCT_OFFSET (NMSettingsClass, connections_loaded),
-	                              NULL, NULL,
-	                              g_cclosure_marshal_VOID__VOID,
-	                              G_TYPE_NONE, 0);
-
 	signals[AGENT_REGISTERED] =
 		g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED,
 		              G_OBJECT_CLASS_TYPE (object_class),