From eb6fe6ae187a228cdf2a06fe18e95307ecba3a40 Mon Sep 17 00:00:00 2001
From: Dan Williams <dcbw@redhat.com>
Date: Mon, 2 Mar 2009 12:58:45 -0500
Subject: [PATCH] system-settings: make default wired connections less confusing

The "Auto ethX" connection that the system settings service creates
for each wired device that does not have an existing backing connection
provided by one of the system settings plugins is now read/write when
at least one plugin has the MODIFY capability.

When the user updates the "Auto ethX" connection, the system settings
service will try to move that connection to a plugin, thereby preserving
the user's changes.  It will also then save that device's MAC address
and never create an "Auto ethX" connection for it again.

diff --git a/marshallers/nm-marshal.list b/marshallers/nm-marshal.list
index 577fbe4..5dc958a 100644
--- a/marshallers/nm-marshal.list
+++ b/marshallers/nm-marshal.list
@@ -17,3 +17,4 @@ VOID:STRING,INT
 VOID:STRING,UINT
 VOID:OBJECT,OBJECT,ENUM
 VOID:POINTER,STRING
+POINTER:POINTER
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a7f5030..5638eab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -9,4 +9,5 @@ src/NetworkManager.c
 src/dhcp-manager/nm-dhcp-dhclient.c
 src/named-manager/nm-named-manager.c
 system-settings/src/main.c
+system-settings/src/nm-default-wired-connection.c
 
diff --git a/system-settings/src/Makefile.am b/system-settings/src/Makefile.am
index 43a4b46..1cb0f79 100644
--- a/system-settings/src/Makefile.am
+++ b/system-settings/src/Makefile.am
@@ -25,6 +25,8 @@ nm_system_settings_SOURCES = \
 	nm-system-config-hal-manager.h \
 	nm-sysconfig-connection.c \
 	nm-sysconfig-connection.h \
+	nm-default-wired-connection.c \
+	nm-default-wired-connection.h \
 	sha1.c \
 	sha1.h
 
diff --git a/system-settings/src/dbus-settings.c b/system-settings/src/dbus-settings.c
index d5b7c26..d098652 100644
--- a/system-settings/src/dbus-settings.c
+++ b/system-settings/src/dbus-settings.c
@@ -101,7 +101,7 @@ load_connections (NMSysconfigSettings *self)
 		// priority plugin.
 
 		for (elt = plugin_connections; elt; elt = g_slist_next (elt))
-			nm_sysconfig_settings_add_connection (self, NM_EXPORTED_CONNECTION (elt->data));
+			nm_sysconfig_settings_add_connection (self, NM_EXPORTED_CONNECTION (elt->data), TRUE);
 
 		g_slist_free (plugin_connections);
 	}
@@ -223,9 +223,9 @@ get_unmanaged_devices (NMSysconfigSettings *self)
 	return devices;
 }
 
-static NMSystemConfigInterface *
-get_first_plugin_by_capability (NMSysconfigSettings *self,
-                                guint32 capability)
+NMSystemConfigInterface *
+nm_sysconfig_settings_get_plugin (NMSysconfigSettings *self,
+                                  guint32 capability)
 {
 	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
@@ -285,7 +285,7 @@ get_property (GObject *object, guint prop_id,
 			g_value_set_static_string (value, "");
 		break;
 	case PROP_CAN_MODIFY:
-		g_value_set_boolean (value, !!get_first_plugin_by_capability (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS));
+		g_value_set_boolean (value, !!nm_sysconfig_settings_get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -391,10 +391,10 @@ nm_sysconfig_settings_new (DBusGConnection *g_conn, NMSystemConfigHalManager *ha
 
 static void
 plugin_connection_added (NMSystemConfigInterface *config,
-					NMExportedConnection *connection,
-					gpointer user_data)
+                         NMExportedConnection *connection,
+                         gpointer user_data)
 {
-	nm_sysconfig_settings_add_connection (NM_SYSCONFIG_SETTINGS (user_data), connection);
+	nm_sysconfig_settings_add_connection (NM_SYSCONFIG_SETTINGS (user_data), connection, TRUE);
 }
 
 static void
@@ -477,7 +477,8 @@ connection_removed (NMExportedConnection *connection,
 
 void
 nm_sysconfig_settings_add_connection (NMSysconfigSettings *self,
-							   NMExportedConnection *connection)
+                                      NMExportedConnection *connection,
+                                      gboolean do_export)
 {
 	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
 
@@ -491,13 +492,16 @@ nm_sysconfig_settings_add_connection (NMSysconfigSettings *self,
 	g_hash_table_insert (priv->connections, g_object_ref (connection), GINT_TO_POINTER (1));
 	g_signal_connect (connection, "removed", G_CALLBACK (connection_removed), self);
 
-	nm_exported_connection_register_object (connection, NM_CONNECTION_SCOPE_SYSTEM, priv->g_connection);
-	nm_settings_signal_new_connection (NM_SETTINGS (self), connection);
+	if (do_export) {
+		nm_exported_connection_register_object (connection, NM_CONNECTION_SCOPE_SYSTEM, priv->g_connection);
+		nm_settings_signal_new_connection (NM_SETTINGS (self), connection);
+	}
 }
 
 void
 nm_sysconfig_settings_remove_connection (NMSysconfigSettings *self,
-								 NMExportedConnection *connection)
+                                         NMExportedConnection *connection,
+                                         gboolean do_signal)
 {
 	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
 
@@ -527,19 +531,67 @@ nm_sysconfig_settings_is_device_managed (NMSysconfigSettings *self,
 	return TRUE;
 }
 
-static gboolean
-impl_settings_add_connection (NMSysconfigSettings *self,
-						GHashTable *hash,
-						DBusGMethodInvocation *context)
+gboolean
+nm_sysconfig_settings_add_new_connection (NMSysconfigSettings *self,
+                                          GHashTable *hash,
+                                          GError **error)
 {
 	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
 	NMConnection *connection;
+	GError *tmp_error = NULL, *last_error = NULL;
 	GSList *iter;
-	GError *err = NULL, *cnfh_error = NULL;
 	gboolean success = FALSE;
 
+	connection = nm_connection_new_from_hash (hash, &tmp_error);
+	if (!connection) {
+		/* Invalid connection hash */
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "Invalid connection: '%s' / '%s' invalid: %d",
+		             tmp_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (tmp_error->domain)) : "(unknown)",
+		             tmp_error ? tmp_error->message : "(unknown)", tmp_error ? tmp_error->code : -1);
+		g_clear_error (&tmp_error);
+		return FALSE;
+	}
+
+	/* Here's how it works:
+	   1) plugin writes a connection.
+	   2) plugin notices that a new connection is available for reading.
+	   3) plugin reads the new connection (the one it wrote in 1) and emits 'connection-added' signal.
+	   4) NMSysconfigSettings receives the signal and adds it to it's connection list.
+	*/
+
+	for (iter = priv->plugins; iter && !success; iter = iter->next) {
+		success = nm_system_config_interface_add_connection (NM_SYSTEM_CONFIG_INTERFACE (iter->data),
+		                                                     connection, &tmp_error);
+		g_clear_error (&last_error);
+		if (!success)
+			last_error = tmp_error;
+	}
+
+	g_object_unref (connection);
+
+	if (!success) {
+		g_set_error (error, NM_SYSCONFIG_SETTINGS_ERROR,
+		             NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED,
+		             "Saving connection failed: (%d) %s",
+		             last_error ? last_error->code : -1,
+		             last_error && last_error->message ? last_error->message : "(unknown)");
+		g_clear_error (&last_error);
+	}
+
+	return success;
+}
+
+static gboolean
+impl_settings_add_connection (NMSysconfigSettings *self,
+                              GHashTable *hash,
+                              DBusGMethodInvocation *context)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GError *err = NULL;
+
 	/* Do any of the plugins support adding? */
-	if (!get_first_plugin_by_capability (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) {
+	if (!nm_sysconfig_settings_get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) {
 		err = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR,
 					    NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED,
 					    "%s", "None of the registered plugins support add.");
@@ -549,40 +601,7 @@ impl_settings_add_connection (NMSysconfigSettings *self,
 	if (!check_polkit_privileges (priv->g_connection, priv->pol_ctx, context, &err))
 		goto out;
 
-	connection = nm_connection_new_from_hash (hash, &cnfh_error);
-	if (connection) {
-		GError *add_error = NULL;
-
-		/* Here's how it works:
-		   1) plugin writes a connection.
-		   2) plugin notices that a new connection is available for reading.
-		   3) plugin reads the new connection (the one it wrote in 1) and emits 'connection-added' signal.
-		   4) NMSysconfigSettings receives the signal and adds it to it's connection list.
-		*/
-
-		success = FALSE;
-		for (iter = priv->plugins; iter && success == FALSE; iter = iter->next) {
-			success = nm_system_config_interface_add_connection (NM_SYSTEM_CONFIG_INTERFACE (iter->data),
-													   connection, &add_error);
-			if (!success && add_error)
-				g_error_free (add_error);
-		}
-
-		g_object_unref (connection);
-
-		if (!success) {
-			err = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR,
-						    NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED,
-						    "%s", "Saving connection failed.");
-		}
-	} else {
-		/* Invalid connection hash */
-		err = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                   "Invalid connection: '%s' / '%s' invalid: %d",
-		                   g_type_name (nm_connection_lookup_setting_type_by_quark (cnfh_error->domain)),
-		                   cnfh_error->message, cnfh_error->code);
-		g_error_free (cnfh_error);
-	}
+	nm_sysconfig_settings_add_new_connection (self, hash, &err);
 
  out:
 	if (err) {
@@ -606,7 +625,7 @@ impl_settings_save_hostname (NMSysconfigSettings *self,
 	gboolean success = FALSE;
 
 	/* Do any of the plugins support setting the hostname? */
-	if (!get_first_plugin_by_capability (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) {
+	if (!nm_sysconfig_settings_get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) {
 		err = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR,
 		                   NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED,
 		                   "%s", "None of the registered plugins support setting the hostname.");
diff --git a/system-settings/src/dbus-settings.h b/system-settings/src/dbus-settings.h
index 23101ab..2e253ab 100644
--- a/system-settings/src/dbus-settings.h
+++ b/system-settings/src/dbus-settings.h
@@ -67,11 +67,14 @@ NMSysconfigSettings *nm_sysconfig_settings_new (DBusGConnection *g_conn,
 void nm_sysconfig_settings_add_plugin     (NMSysconfigSettings *settings,
 					   NMSystemConfigInterface *plugin);
 
+/* Registers an exising connection with the settings service */
 void nm_sysconfig_settings_add_connection (NMSysconfigSettings *settings,
-                                           NMExportedConnection *connection);
+                                           NMExportedConnection *connection,
+                                           gboolean do_export);
 
 void nm_sysconfig_settings_remove_connection (NMSysconfigSettings *settings,
-                                              NMExportedConnection *connection);
+                                              NMExportedConnection *connection,
+                                              gboolean do_signal);
 
 void nm_sysconfig_settings_update_unamanged_devices (NMSysconfigSettings *settings,
                                                      GSList *new_list);
@@ -79,4 +82,14 @@ void nm_sysconfig_settings_update_unamanged_devices (NMSysconfigSettings *settin
 gboolean nm_sysconfig_settings_is_device_managed (NMSysconfigSettings *settings,
                                                   const char *udi);
 
+NMSystemConfigInterface *nm_sysconfig_settings_get_plugin (NMSysconfigSettings *self,
+                                                           guint32 capability);
+
+/* Adds a new connection from a hash of that connection's settings,
+ * potentially saving the new connection to persistent storage.
+ */
+gboolean nm_sysconfig_settings_add_new_connection (NMSysconfigSettings *self,
+                                                   GHashTable *hash,
+                                                   GError **error);
+
 #endif  /* __DBUS_SETTINGS_H__ */
diff --git a/system-settings/src/main.c b/system-settings/src/main.c
index e0021cd..20866d6 100644
--- a/system-settings/src/main.c
+++ b/system-settings/src/main.c
@@ -47,6 +47,9 @@
 #include "dbus-settings.h"
 #include "nm-system-config-hal-manager.h"
 #include "nm-system-config-interface.h"
+#include "nm-default-wired-connection.h"
+
+#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default"
 
 typedef struct {
 	DBusConnection *connection;
@@ -59,6 +62,8 @@ typedef struct {
 	GMainLoop *loop;
 
 	GHashTable *wired_devices;
+
+	const char *config;
 } Application;
 
 
@@ -202,11 +207,11 @@ load_stuff (gpointer user_data)
 
 typedef struct {
 	Application *app;
-	NMExportedConnection *connection;
+	NMDefaultWiredConnection *connection;
 	guint add_id;
+	guint updated_id;
+	guint deleted_id;
 	char *udi;
-	GByteArray *mac;
-	char *iface;
 } WiredDeviceInfo;
 
 static void
@@ -214,13 +219,16 @@ wired_device_info_destroy (gpointer user_data)
 {
 	WiredDeviceInfo *info = (WiredDeviceInfo *) user_data;
 
-	g_free (info->iface);
-	if (info->mac)
-		g_byte_array_free (info->mac, TRUE);
 	if (info->add_id)
 		g_source_remove (info->add_id);
+	if (info->updated_id)
+		g_source_remove (info->updated_id);
+	if (info->deleted_id)
+		g_source_remove (info->deleted_id);
 	if (info->connection) {
-		nm_sysconfig_settings_remove_connection (info->app->settings, info->connection);
+		nm_sysconfig_settings_remove_connection (info->app->settings,
+		                                         NM_EXPORTED_CONNECTION (info->connection),
+		                                         TRUE);
 		g_object_unref (info->connection);
 	}
 	g_free (info);
@@ -292,9 +300,7 @@ have_connection_for_device (Application *app, GByteArray *mac)
 	g_return_val_if_fail (app != NULL, FALSE);
 	g_return_val_if_fail (mac != NULL, FALSE);
 
-	/* If the device doesn't have a connection advertised by any of the
-	 * plugins, create a new default DHCP-enabled connection for it.
-	 */
+	/* Find a wired connection locked to the given MAC address, if any */
 	list = nm_settings_list_connections (NM_SETTINGS (app->settings));
 	for (iter = list; iter; iter = g_slist_next (iter)) {
 		NMExportedConnection *exported = NM_EXPORTED_CONNECTION (iter->data);
@@ -340,76 +346,240 @@ have_connection_for_device (Application *app, GByteArray *mac)
 	return ret;
 }
 
+/* Search through the list of blacklisted MAC addresses in the config file. */
 static gboolean
-add_default_dhcp_connection (gpointer user_data)
+is_mac_auto_wired_blacklisted (const GByteArray *mac, const char *filename)
+{
+	GKeyFile *config;
+	char **list, **iter;
+	gboolean found = FALSE;
+
+	g_return_val_if_fail (mac != NULL, FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+
+	config = g_key_file_new ();
+	if (!config) {
+		g_warning ("%s: not enough memory to load config file.", __func__);
+		return FALSE;
+	}
+
+	g_key_file_set_list_separator (config, ',');
+	if (!g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, NULL))
+		goto out;
+
+	list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, NULL, NULL);
+	for (iter = list; iter && *iter; iter++) {
+		struct ether_addr *candidate;
+
+		candidate = ether_aton (*iter);
+		if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (list)
+		g_strfreev (list);
+
+out:
+	g_key_file_free (config);
+	return found;
+}
+
+static void
+default_wired_deleted (NMDefaultWiredConnection *wired,
+                       const GByteArray *mac,
+                       WiredDeviceInfo *info)
 {
-	WiredDeviceInfo *info = (WiredDeviceInfo *) user_data;
-	NMSettingConnection *s_con;
-	NMSettingWired *s_wired;
 	NMConnection *wrapped;
-	GByteArray *setting_mac;
-	char *id;
-	char *uuid;
+	NMSettingConnection *s_con;
+	char *tmp;
+	GKeyFile *config;
+	char **list, **iter, **updated;
+	gboolean found = FALSE;
+	gsize len = 0;
+	char *data;
+
+	/* If there was no config file specified, there's nothing to do */
+	if (!info->app->config)
+		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.
+	 */
 
-	if (info->add_id)
-		info->add_id = 0;
+	wrapped = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (wired));
+	g_assert (wrapped);
+	s_con = (NMSettingConnection *) nm_connection_get_setting (wrapped, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
 
-	/* If the device isn't managed, ignore it */
-	if (!nm_sysconfig_settings_is_device_managed (info->app->settings, info->udi))
-		goto ignore;
+	/* Ignore removals of read-only connections, since they couldn't have
+	 * been removed by the user.
+	 */
+	if (nm_setting_connection_get_read_only (s_con))
+		goto cleanup;
+
+	config = g_key_file_new ();
+	if (!config)
+		goto cleanup;
+
+	g_key_file_set_list_separator (config, ',');
+	g_key_file_load_from_file (config, info->app->config, G_KEY_FILE_KEEP_COMMENTS, NULL);
 
-	if (!info->iface) {
-		struct ether_addr mac;
+	list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL);
+	/* Traverse entire list to get count of # items */
+	for (iter = list; iter && *iter; iter++) {
+		struct ether_addr *candidate;
 
-		info->iface = get_details_for_udi (info->app, info->udi, &mac);
-		if (!info->iface)
-			goto ignore;
-		info->mac = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (info->mac, mac.ether_addr_octet, ETH_ALEN);
+		candidate = ether_aton (*iter);
+		if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN))
+			found = TRUE;
 	}
 
-	if (have_connection_for_device (info->app, info->mac))
-		goto ignore;
+	/* Add this device's MAC to the list */
+	if (!found) {
+		tmp = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x",
+		                       mac->data[0], mac->data[1], mac->data[2],
+		                       mac->data[3], mac->data[4], mac->data[5]);
+
+		updated = g_malloc0 (sizeof (char*) * (len + 2));
+		if (list && len)
+			memcpy (updated, list, len);
+		updated[len] = tmp;
+
+		g_key_file_set_string_list (config,
+		                            "main", CONFIG_KEY_NO_AUTO_DEFAULT,
+		                            (const char **) updated,
+		                            len + 1);
+		/* 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 (info->app->config, data, len, NULL);
+			g_free (data);
+		}
+	}
 
-	wrapped = nm_connection_new ();
-	info->connection = nm_exported_connection_new (wrapped);
-	g_object_unref (wrapped);
+	if (list)
+		g_strfreev (list);
+	g_key_file_free (config);
 
-	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+cleanup:
+	/* Clear the connection first so that a 'removed' signal doesn't get emitted
+	 * during wired_device_info_destroy(), becuase this connection removal
+	 * is expected and already handled.
+	 */
+	g_object_unref (wired);
+	info->connection = NULL;
 
-	id = g_strdup_printf (_("Auto %s"), info->iface);
-	uuid = nm_utils_uuid_generate ();
+	g_hash_table_remove (info->app->wired_devices, info->udi);
+}
 
-	g_object_set (s_con,
-		      NM_SETTING_CONNECTION_ID, id,
-		      NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
-		      NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
-		      NM_SETTING_CONNECTION_UUID, uuid,
-		      NM_SETTING_CONNECTION_READ_ONLY, TRUE,
-		      NULL);
+static GError *
+default_wired_try_update (NMDefaultWiredConnection *wired,
+                          GHashTable *new_settings,
+                          WiredDeviceInfo *info)
+{
+	GError *error = NULL;
+	NMConnection *wrapped;
+	NMSettingConnection *s_con;
+	const char *id;
 
-	nm_connection_add_setting (wrapped, NM_SETTING (s_con));
+	/* Try to move this default wired conneciton to a plugin so that it has
+	 * persistent storage.
+	 */
 
-	g_message ("Adding default connection '%s' for %s", id, info->udi);
-		
-	g_free (id);
-	g_free (uuid);
+	wrapped = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (wired));
+	g_assert (wrapped);
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (wrapped, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+	id = nm_setting_connection_get_id (s_con);
+	g_assert (id);
+
+	nm_sysconfig_settings_remove_connection (info->app->settings, NM_EXPORTED_CONNECTION (wired), FALSE);
+	if (nm_sysconfig_settings_add_new_connection (info->app->settings, new_settings, &error)) {
+		g_message ("Saved default wired connection '%s' to persistent storage", id);
+		return NULL;
+	}
 
-	/* Lock the connection to this device */
-	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+	g_warning ("%s: couldn't save default wired connection '%s': %d / %s",
+	           __func__, id, error ? error->code : -1,
+	           (error && error->message) ? error->message : "(unknown)");
 
-	setting_mac = g_byte_array_sized_new (ETH_ALEN);
-	g_byte_array_append (setting_mac, info->mac->data, ETH_ALEN);
-	g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, setting_mac, NULL);
-	g_byte_array_free (setting_mac, TRUE);
+	/* 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.
+	 */
+	nm_sysconfig_settings_add_connection (info->app->settings, NM_EXPORTED_CONNECTION (wired), FALSE);
 
-	nm_connection_add_setting (wrapped, NM_SETTING (s_wired));
+	return error;
+}
 
-	nm_sysconfig_settings_add_connection (info->app->settings, info->connection);
+static gboolean
+add_default_wired_connection (gpointer user_data)
+{
+	WiredDeviceInfo *info = (WiredDeviceInfo *) user_data;
+	GByteArray *mac = NULL;
+	struct ether_addr tmp;
+	char *iface = NULL;
+	NMSettingConnection *s_con;
+	NMConnection *wrapped;
+	gboolean read_only = TRUE;
+	const char *id;
+
+	info->add_id = 0;
+	g_assert (info->connection == NULL);
+
+	/* If the device isn't managed, ignore it */
+	if (!nm_sysconfig_settings_is_device_managed (info->app->settings, info->udi))
+		goto ignore;
+
+	iface = get_details_for_udi (info->app, info->udi, &tmp);
+	if (!iface)
+		goto ignore;
+
+	mac = g_byte_array_sized_new (ETH_ALEN);
+	g_byte_array_append (mac, tmp.ether_addr_octet, ETH_ALEN);
+
+	if (have_connection_for_device (info->app, mac))
+		goto ignore;
+
+	if (info->app->config && is_mac_auto_wired_blacklisted (mac, info->app->config))
+		goto ignore;
+
+	if (nm_sysconfig_settings_get_plugin (info->app->settings, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS))
+		read_only = FALSE;
+
+	info->connection = nm_default_wired_connection_new (mac, iface, read_only);
+	if (!info->connection)
+		goto ignore;
 
+	wrapped = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (info->connection));
+	g_assert (wrapped);
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (wrapped, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+	id = nm_setting_connection_get_id (s_con);
+	g_assert (id);
+
+	g_message ("Added default wired connection '%s' for %s", id, info->udi);
+
+	info->updated_id = g_signal_connect (info->connection, "try-update",
+	                                     (GCallback) default_wired_try_update, info);
+	info->deleted_id = g_signal_connect (info->connection, "deleted",
+	                                     (GCallback) default_wired_deleted, info);
+	nm_sysconfig_settings_add_connection (info->app->settings,
+	                                      NM_EXPORTED_CONNECTION (info->connection),
+	                                      TRUE);
 	return FALSE;
 
 ignore:
+	if (mac)
+		g_byte_array_free (mac, TRUE);
+	g_free (iface);
 	g_hash_table_remove (info->app->wired_devices, info->udi);
 	return FALSE;
 }
@@ -426,7 +596,7 @@ device_added_cb (DBusGProxy *proxy, const char *udi, NMDeviceType devtype, gpoin
 	/* Wait for a plugin to figure out if the device should be managed or not */
 	info = g_malloc0 (sizeof (WiredDeviceInfo));
 	info->app = app;
-	info->add_id = g_timeout_add (4000, add_default_dhcp_connection, info);
+	info->add_id = g_timeout_add_seconds (4, add_default_wired_connection, info);
 	info->udi = g_strdup (udi);
 	g_hash_table_insert (app->wired_devices, info->udi, info);
 }
@@ -637,7 +807,8 @@ main (int argc, char **argv)
 	g_option_context_free (opt_ctx);
 
 	if (config) {
-		if (!parse_config_file (config, &plugins, &error)) {
+		app->config = config;
+		if (!parse_config_file (app->config, &plugins, &error)) {
 			g_warning ("Invalid config file: %s.", error->message);
 			return 1;
 		}
diff --git a/system-settings/src/nm-default-wired-connection.c b/system-settings/src/nm-default-wired-connection.c
new file mode 100644
index 0000000..c8a6daa
--- /dev/null
+++ b/system-settings/src/nm-default-wired-connection.c
@@ -0,0 +1,355 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ * (C) Copyright 2009 Red Hat, Inc.
+ */
+
+#include <netinet/ether.h>
+
+#include <glib/gi18n.h>
+
+#include <NetworkManager.h>
+#include <nm-settings.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-utils.h>
+
+#include "nm-dbus-glib-types.h"
+#include "nm-marshal.h"
+#include "nm-default-wired-connection.h"
+
+G_DEFINE_TYPE (NMDefaultWiredConnection, nm_default_wired_connection, NM_TYPE_SYSCONFIG_CONNECTION)
+
+#define NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionPrivate))
+
+typedef struct {
+	char *iface;
+	GByteArray *mac;
+	gboolean read_only;
+} NMDefaultWiredConnectionPrivate;
+
+enum {
+	PROP_0,
+	PROP_MAC,
+	PROP_IFACE,
+	PROP_READ_ONLY,
+	LAST_PROP
+};
+
+enum {
+	TRY_UPDATE,
+	DELETED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+NMDefaultWiredConnection *
+nm_default_wired_connection_new (const GByteArray *mac,
+                                 const char *iface,
+                                 gboolean read_only)
+{
+
+	g_return_val_if_fail (mac != NULL, NULL);
+	g_return_val_if_fail (mac->len == ETH_ALEN, NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	return g_object_new (NM_TYPE_DEFAULT_WIRED_CONNECTION,
+	                     NM_DEFAULT_WIRED_CONNECTION_MAC, mac,
+	                     NM_DEFAULT_WIRED_CONNECTION_IFACE, iface,
+	                     NM_DEFAULT_WIRED_CONNECTION_READ_ONLY, read_only,
+	                     NULL);
+}
+
+static GByteArray *
+dup_wired_mac (NMExportedConnection *exported)
+{
+	NMConnection *wrapped;
+	NMSettingWired *s_wired;
+	const GByteArray *mac;
+	GByteArray *dup;
+
+	wrapped = nm_exported_connection_get_connection (exported);
+	if (!wrapped)
+		return NULL;
+
+	s_wired = (NMSettingWired *) nm_connection_get_setting (wrapped, NM_TYPE_SETTING_WIRED);
+	if (!s_wired)
+		return NULL;
+
+	mac = nm_setting_wired_get_mac_address (s_wired);	
+	if (!mac || (mac->len != ETH_ALEN))
+		return NULL;
+
+	dup = g_byte_array_sized_new (ETH_ALEN);
+	g_byte_array_append (dup, mac->data, ETH_ALEN);
+	return dup;
+}
+
+static gboolean
+update (NMExportedConnection *exported,
+        GHashTable *new_settings,
+        GError **error)
+{
+	NMDefaultWiredConnection *connection = NM_DEFAULT_WIRED_CONNECTION (exported);
+	gboolean success;
+	GByteArray *mac;
+
+	/* Ensure object stays alive across signal emission */
+	g_object_ref (exported);
+
+	/* Save a copy of the current MAC address just in case the user
+	 * changed it when updating the connection.
+	 */
+	mac = dup_wired_mac (exported);
+
+	/* Let NMSysconfigConnection check permissions */
+	success = NM_EXPORTED_CONNECTION_CLASS (nm_default_wired_connection_parent_class)->update (exported, new_settings, error);
+	if (success) {
+		g_signal_emit_by_name (connection, "try-update", new_settings, error);
+		success = *error ? FALSE : TRUE;
+
+		if (success)
+			g_signal_emit (connection, signals[DELETED], 0, mac);
+	}
+
+	g_byte_array_free (mac, TRUE);
+	g_object_unref (exported);
+	return success;
+}
+
+static gboolean
+do_delete (NMExportedConnection *exported, GError **error)
+{
+	gboolean success;
+	GByteArray *mac;
+
+	g_object_ref (exported);
+	mac = dup_wired_mac (exported);
+
+	success = NM_EXPORTED_CONNECTION_CLASS (nm_default_wired_connection_parent_class)->do_delete (exported, error);
+	if (success)
+		g_signal_emit (exported, signals[DELETED], 0, mac);
+	
+	g_byte_array_free (mac, TRUE);
+	g_object_unref (exported);
+	return success;
+}
+
+static void
+nm_default_wired_connection_init (NMDefaultWiredConnection *self)
+{
+}
+
+static GObject *
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMDefaultWiredConnectionPrivate *priv;
+	NMConnection *wrapped;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	char *id, *uuid;
+
+	object = G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->constructor (type, n_construct_params, construct_params);
+	if (!object)
+		return NULL;
+
+	priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+
+	wrapped = nm_connection_new ();
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+	id = g_strdup_printf (_("Auto %s"), priv->iface);
+	uuid = nm_utils_uuid_generate ();
+
+	g_object_set (s_con,
+		      NM_SETTING_CONNECTION_ID, id,
+		      NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+		      NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+		      NM_SETTING_CONNECTION_UUID, uuid,
+		      NM_SETTING_CONNECTION_READ_ONLY, priv->read_only,
+		      NULL);
+
+	g_free (id);
+	g_free (uuid);
+
+	nm_connection_add_setting (wrapped, NM_SETTING (s_con));
+
+	/* Lock the connection to the specific device */
+	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+	g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->mac, NULL);
+	nm_connection_add_setting (wrapped, NM_SETTING (s_wired));
+
+	g_object_set (object, NM_EXPORTED_CONNECTION_CONNECTION, wrapped, NULL);
+	g_object_unref (wrapped);
+
+	return object;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+
+	g_free (priv->iface);
+	g_byte_array_free (priv->mac, TRUE);
+
+	G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MAC:
+		g_value_set_pointer (value, priv->mac);
+		break;
+	case PROP_IFACE:
+		g_value_set_string (value, priv->iface);
+		break;
+	case PROP_READ_ONLY:
+		g_value_set_boolean (value, priv->read_only);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+	GByteArray *array;
+
+	switch (prop_id) {
+	case PROP_MAC:
+		/* Construct only */
+		array = g_value_get_pointer (value);
+		if (priv->mac) {
+			g_byte_array_free (priv->mac, TRUE);
+			priv->mac = NULL;
+		}
+		if (array) {
+			g_return_if_fail (array->len == ETH_ALEN);
+			priv->mac = g_byte_array_sized_new (array->len);
+			g_byte_array_append (priv->mac, array->data, ETH_ALEN);
+		}
+		break;
+	case PROP_IFACE:
+		g_free (priv->iface);
+		priv->iface = g_value_dup_string (value);
+		break;
+	case PROP_READ_ONLY:
+		priv->read_only = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static gboolean
+try_update_signal_accumulator (GSignalInvocationHint *ihint,
+                               GValue *return_accu,
+                               const GValue *handler_return,
+                               gpointer data)
+{
+	gpointer new_ptr = g_value_get_pointer (handler_return);
+
+	g_value_set_pointer (return_accu, new_ptr);
+
+	/* Continue if no error was returned from the handler */
+	return new_ptr ? FALSE : TRUE;
+}
+
+static void
+nm_default_wired_connection_class_init (NMDefaultWiredConnectionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMExportedConnectionClass *exported_class = NM_EXPORTED_CONNECTION_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMDefaultWiredConnectionPrivate));
+
+	/* Virtual methods */
+	object_class->constructor = constructor;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	exported_class->update = update;
+	exported_class->do_delete = do_delete;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_MAC,
+		 g_param_spec_pointer (NM_DEFAULT_WIRED_CONNECTION_MAC,
+		                       "MAC",
+		                       "MAC Address",
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_IFACE,
+		 g_param_spec_string (NM_DEFAULT_WIRED_CONNECTION_IFACE,
+		                       "Iface",
+		                       "Interface",
+		                       NULL,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_READ_ONLY,
+		 g_param_spec_boolean (NM_DEFAULT_WIRED_CONNECTION_READ_ONLY,
+		                       "ReadOnly",
+		                       "Read Only",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/* Signals */
+	signals[TRY_UPDATE] =
+		g_signal_new ("try-update",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, try_update_signal_accumulator, NULL,
+			      _nm_marshal_POINTER__POINTER,
+			      G_TYPE_POINTER, 1,
+			      DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT);
+
+	/* The 'deleted' signal is used to signal intentional deletions (like
+	 * updating or user-requested deletion) rather than using the
+	 * NMExportedConnection superclass' 'removed' signal, since that signal
+	 * doesn't have the semantics we want; it gets emitted as a side-effect
+	 * of various operations and is meant more for D-Bus clients instead
+	 * of in-service uses.
+	 */
+	signals[DELETED] =
+		g_signal_new ("deleted",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      g_cclosure_marshal_VOID__POINTER,
+			      G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
diff --git a/system-settings/src/nm-default-wired-connection.h b/system-settings/src/nm-default-wired-connection.h
new file mode 100644
index 0000000..50c8b18
--- /dev/null
+++ b/system-settings/src/nm-default-wired-connection.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ * (C) Copyright 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_DEFAULT_WIRED_CONNECTION_H
+#define NM_DEFAULT_WIRED_CONNECTION_H
+
+#include <nm-settings.h>
+#include "nm-sysconfig-connection.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEFAULT_WIRED_CONNECTION            (nm_default_wired_connection_get_type ())
+#define NM_DEFAULT_WIRED_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnection))
+#define NM_DEFAULT_WIRED_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass))
+#define NM_IS_DEFAULT_WIRED_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION))
+#define NM_IS_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION))
+#define NM_DEFAULT_WIRED_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass))
+
+#define NM_DEFAULT_WIRED_CONNECTION_MAC   "mac"
+#define NM_DEFAULT_WIRED_CONNECTION_IFACE "iface"
+#define NM_DEFAULT_WIRED_CONNECTION_READ_ONLY "read-only"
+
+typedef struct {
+	NMSysconfigConnection parent;
+} NMDefaultWiredConnection;
+
+typedef struct {
+	NMSysconfigConnectionClass parent;
+} NMDefaultWiredConnectionClass;
+
+GType nm_default_wired_connection_get_type (void);
+
+NMDefaultWiredConnection *nm_default_wired_connection_new (const GByteArray *mac,
+                                                           const char *iface,
+                                                           gboolean read_only);
+
+G_END_DECLS
+
+#endif /* NM_DEFAULT_WIRED_CONNECTION_H */
-- 
1.6.0.2

