summary refs log tree commit diff
path: root/src/settings
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-07-14 19:38:58 +0200
committerMichael Biebl <biebl@debian.org>2015-07-14 19:38:58 +0200
commit50a58f0fabd8a34c1b6108a107e08abe3c1ccd24 (patch)
tree6790165f39daee79e2b6c6617483320613493367 /src/settings
parentf408e27bccfacf347605a8d98649975a68f38a17 (diff)
Imported Upstream version 1.0.4 upstream/1.0.4
Diffstat (limited to 'src/settings')
-rw-r--r--src/settings/nm-settings-connection.c8
-rw-r--r--src/settings/nm-settings.c65
-rw-r--r--src/settings/nm-settings.h2
-rw-r--r--src/settings/plugins/Makefile.in2
-rw-r--r--src/settings/plugins/example/Makefile.in2
-rw-r--r--src/settings/plugins/example/plugin.c2
-rw-r--r--src/settings/plugins/ibft/Makefile.in2
-rw-r--r--src/settings/plugins/ibft/reader.c46
-rw-r--r--src/settings/plugins/ibft/tests/Makefile.in437
-rw-r--r--src/settings/plugins/ifcfg-rh/Makefile.in2
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c15
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h3
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c59
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c48
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.h3
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c78
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h6
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/Makefile.in447
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c35
-rw-r--r--src/settings/plugins/ifcfg-rh/utils.c144
-rw-r--r--src/settings/plugins/ifcfg-rh/utils.h3
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c28
-rw-r--r--src/settings/plugins/ifcfg-suse/Makefile.in2
-rw-r--r--src/settings/plugins/ifnet/Makefile.in2
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c8
-rw-r--r--src/settings/plugins/ifnet/net_parser.c4
-rw-r--r--src/settings/plugins/ifnet/net_parser.h1
-rw-r--r--src/settings/plugins/ifnet/plugin.c27
-rw-r--r--src/settings/plugins/ifnet/tests/Makefile.in2
-rw-r--r--src/settings/plugins/ifnet/tests/test_all.c2
-rw-r--r--src/settings/plugins/ifupdown/Makefile.in2
-rw-r--r--src/settings/plugins/ifupdown/plugin.c27
-rw-r--r--src/settings/plugins/ifupdown/tests/Makefile.in437
-rw-r--r--src/settings/plugins/keyfile/Makefile.in2
-rw-r--r--src/settings/plugins/keyfile/common.h2
-rw-r--r--src/settings/plugins/keyfile/plugin.c40
-rw-r--r--src/settings/plugins/keyfile/reader.c1349
-rw-r--r--src/settings/plugins/keyfile/tests/Makefile.in440
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.in2
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob4
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old6
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c108
-rw-r--r--src/settings/plugins/keyfile/utils.c223
-rw-r--r--src/settings/plugins/keyfile/utils.h51
-rw-r--r--src/settings/plugins/keyfile/writer.c807
-rw-r--r--src/settings/plugins/keyfile/writer.h1
47 files changed, 2088 insertions, 2900 deletions
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index ec2052f0..0f67a216 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -96,6 +96,8 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
+	gboolean removed;
+
 	NMAgentManager *agent_mgr;
 	NMSessionMonitor *session_monitor;
 	guint session_changed_id;
@@ -1742,6 +1744,12 @@ impl_settings_connection_clear_secrets (NMSettingsConnection *self,
 void
 nm_settings_connection_signal_remove (NMSettingsConnection *self)
 {
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	if (priv->removed)
+		g_return_if_reached ();
+	priv->removed = TRUE;
+
 	/* Emit removed first */
 	g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED);
 
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 9d40b06e..4d7d697d 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -94,9 +94,10 @@ EXPORT(nm_settings_connection_replace_settings)
 EXPORT(nm_settings_connection_replace_and_commit)
 /* END LINKER CRACKROCK */
 
+#define PLUGIN_MODULE_PATH      "plugin-module-path"
+
 static void claim_connection (NMSettings *self,
-                              NMSettingsConnection *connection,
-                              gboolean do_export);
+                              NMSettingsConnection *connection);
 
 static gboolean impl_settings_list_connections (NMSettings *self,
                                                 GPtrArray **connections,
@@ -218,7 +219,7 @@ plugin_connection_added (NMSystemConfigInterface *config,
                          NMSettingsConnection *connection,
                          gpointer user_data)
 {
-	claim_connection (NM_SETTINGS (user_data), connection, TRUE);
+	claim_connection (NM_SETTINGS (user_data), connection);
 }
 
 static void
@@ -239,7 +240,7 @@ load_connections (NMSettings *self)
 		// priority plugin.
 
 		for (elt = plugin_connections; elt; elt = g_slist_next (elt))
-			claim_connection (self, NM_SETTINGS_CONNECTION (elt->data), TRUE);
+			claim_connection (self, NM_SETTINGS_CONNECTION (elt->data));
 
 		g_slist_free (plugin_connections);
 
@@ -468,6 +469,21 @@ notify (GObject *object, GParamSpec *pspec)
 	g_slice_free (GValue, value);
 }
 
+gboolean
+nm_settings_has_connection (NMSettings *self, NMConnection *connection)
+{
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GHashTableIter iter;
+	gpointer data;
+
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &data))
+		if (data == connection)
+			return TRUE;
+
+	return FALSE;
+}
+
 const GSList *
 nm_settings_get_unmanaged_specs (NMSettings *self)
 {
@@ -489,7 +505,7 @@ get_plugin (NMSettings *self, guint32 capability)
 		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
 
 		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
-		if (caps & capability)
+		if (NM_FLAGS_ALL (caps, capability))
 			return NM_SYSTEM_CONFIG_INTERFACE (iter->data);
 	}
 
@@ -596,6 +612,7 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
 	NMSettingsPrivate *priv;
 	char *pname = NULL;
 	char *pinfo = NULL;
+	const char *path;
 
 	g_return_if_fail (NM_IS_SETTINGS (self));
 	g_return_if_fail (NM_IS_SYSTEM_CONFIG_INTERFACE (plugin));
@@ -613,7 +630,10 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
 	              NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo,
 	              NULL);
 
-	nm_log_info (LOGD_SETTINGS, "Loaded plugin %s: %s", pname, pinfo);
+	path = g_object_get_data (G_OBJECT (plugin), PLUGIN_MODULE_PATH);
+
+	nm_log_info (LOGD_SETTINGS, "Loaded settings plugin %s: %s%s%s%s", pname, pinfo,
+	             NM_PRINT_FMT_QUOTED (path, " (", path, ")", ""));
 	g_free (pname);
 	g_free (pinfo);
 }
@@ -665,17 +685,13 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 		GModule *plugin;
 		gs_free char *full_name = NULL;
 		gs_free char *path = NULL;
-		gs_free char *pname = NULL;
+		const char *pname;
 		GObject *obj;
 		GObject * (*factory_func) (void);
 		struct stat st;
 		int errsv;
 
-		pname = g_strdup (*iter);
-		g_strstrip (pname);
-
-		if (!*pname)
-			continue;
+		pname = *iter;
 
 		if (!*pname || strchr (pname, '/')) {
 			LOG (LOGL_WARN, "ignore invalid plugin \"%s\"", pname);
@@ -719,7 +735,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 		plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
 		if (!plugin) {
 			LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s",
-			     pname, full_name, g_module_error ());
+			     pname, path, g_module_error ());
 			continue;
 		}
 
@@ -730,6 +746,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			             "Could not find plugin '%s' factory function.",
 			             pname);
 			success = FALSE;
+			g_module_close (plugin);
 			break;
 		}
 
@@ -739,11 +756,14 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			             "Plugin '%s' returned invalid system config object.",
 			             pname);
 			success = FALSE;
+			g_module_close (plugin);
 			break;
 		}
 
 		g_module_make_resident (plugin);
 		g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
+		g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
+		path = NULL;
 		add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj));
 		list = g_slist_append (list, obj);
 	}
@@ -794,6 +814,11 @@ static void
 connection_removed (NMSettingsConnection *connection, gpointer user_data)
 {
 	NMSettings *self = NM_SETTINGS (user_data);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	const char *cpath = nm_connection_get_path (NM_CONNECTION (connection));
+
+	if (!g_hash_table_lookup (priv->connections, cpath))
+		g_return_if_reached ();
 
 	g_object_ref (connection);
 
@@ -809,8 +834,7 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_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)));
+	g_hash_table_remove (priv->connections, (gpointer) cpath);
 
 	/* Notify D-Bus */
 	g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
@@ -877,9 +901,7 @@ openconnect_migrate_hack (NMConnection *connection)
 }
 
 static void
-claim_connection (NMSettings *self,
-                  NMSettingsConnection *connection,
-                  gboolean do_export)
+claim_connection (NMSettings *self, NMSettingsConnection *connection)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	static guint32 ec_counter = 0;
@@ -1030,7 +1052,7 @@ nm_settings_add_connection (NMSettings *self,
 
 		added = nm_system_config_interface_add_connection (plugin, connection, save_to_disk, &add_error);
 		if (added) {
-			claim_connection (self, added, TRUE);
+			claim_connection (self, added);
 			return added;
 		}
 		nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s",
@@ -1576,6 +1598,9 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 		NMConnection *connection = NM_CONNECTION (data);
 		const char *ctype, *iface;
 
+		if (!nm_device_check_connection_compatible (device, connection))
+			continue;
+
 		s_con = nm_connection_get_setting_connection (connection);
 
 		iface = nm_setting_connection_get_interface_name (s_con);
@@ -1671,7 +1696,7 @@ default_wired_clear_tag (NMSettings *self,
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
 
 	if (add_to_no_auto_default)
-		nm_config_set_ethernet_no_auto_default (NM_SETTINGS_GET_PRIVATE (self)->config, device);
+		nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device);
 }
 
 void
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 9c0a4c83..d196efe6 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -111,6 +111,8 @@ NMSettingsConnection *nm_settings_get_connection_by_path (NMSettings *settings,
 NMSettingsConnection *nm_settings_get_connection_by_uuid (NMSettings *settings,
                                                           const char *uuid);
 
+gboolean nm_settings_has_connection (NMSettings *self, NMConnection *connection);
+
 const GSList *nm_settings_get_unmanaged_specs (NMSettings *self);
 
 char *nm_settings_get_hostname (NMSettings *self);
diff --git a/src/settings/plugins/Makefile.in b/src/settings/plugins/Makefile.in
index ea45e2a5..d7b7ab14 100644
--- a/src/settings/plugins/Makefile.in
+++ b/src/settings/plugins/Makefile.in
@@ -200,6 +200,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -313,6 +314,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/example/Makefile.in b/src/settings/plugins/example/Makefile.in
index dabbdc74..3b7a1551 100644
--- a/src/settings/plugins/example/Makefile.in
+++ b/src/settings/plugins/example/Makefile.in
@@ -191,6 +191,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -304,6 +305,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/example/plugin.c b/src/settings/plugins/example/plugin.c
index 9945e13b..0bcb38fc 100644
--- a/src/settings/plugins/example/plugin.c
+++ b/src/settings/plugins/example/plugin.c
@@ -847,7 +847,7 @@ nm_system_config_factory (void)
 		priv = SC_PLUGIN_EXAMPLE_GET_PRIVATE (singleton);
 
 		/* Cache the config file path */
-		priv->conf_file = nm_config_get_path (nm_config_get ());
+		priv->conf_file = nm_config_data_get_config_main_file (nm_config_get_data (nm_config_get ()));
 	} else {
 		/* This function should never be called twice */
 		g_assert_not_reached ();
diff --git a/src/settings/plugins/ibft/Makefile.in b/src/settings/plugins/ibft/Makefile.in
index 405c213b..de9be286 100644
--- a/src/settings/plugins/ibft/Makefile.in
+++ b/src/settings/plugins/ibft/Makefile.in
@@ -266,6 +266,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -379,6 +380,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ibft/reader.c b/src/settings/plugins/ibft/reader.c
index e89d9b08..9bc63462 100644
--- a/src/settings/plugins/ibft/reader.c
+++ b/src/settings/plugins/ibft/reader.c
@@ -36,7 +36,6 @@
 
 #include "nm-core-internal.h"
 #include "nm-platform.h"
-#include "nm-posix-signals.h"
 #include "NetworkManagerUtils.h"
 #include "nm-logging.h"
 
@@ -44,22 +43,6 @@
 
 #define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, "    " msg)
 
-static void
-iscsiadm_child_setup (gpointer user_data G_GNUC_UNUSED)
-{
-	/* We are in the child process here; set a different process group to
-	 * ensure signal isolation between child and parent.
-	 */
-	pid_t pid = getpid ();
-	setpgid (pid, pid);
-
-	/*
-	 * We blocked signals in main(). We need to restore original signal
-	 * mask for iscsiadm here so that it can receive signals.
-	 */
-	nm_unblock_posix_signals (NULL);
-}
-
 /* Removes trailing whitespace and whitespace before and immediately after the '=' */
 static char *
 remove_most_whitespace (const char *src)
@@ -126,7 +109,7 @@ read_ibft_blocks (const char *iscsiadm_path,
 	g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE);
 
 	if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0,
-	                   iscsiadm_child_setup, NULL, &out, &err, &status, error))
+	                   NULL, NULL, &out, &err, &status, error))
 		goto done;
 
 	if (!WIFEXITED (status)) {
@@ -136,6 +119,15 @@ read_ibft_blocks (const char *iscsiadm_path,
 	}
 
 	if (WEXITSTATUS (status) != 0) {
+		if (err) {
+			char *nl;
+
+			/* the error message contains newlines. concatenate the lines with whitespace */
+			for (nl = err; *nl; nl++) {
+				if (*nl == '\n')
+					*nl = ' ';
+			}
+		}
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "iBFT: %s exited with error %d.  Message: '%s'",
 		             iscsiadm_path, WEXITSTATUS (status), err ? err : "(none)");
@@ -405,13 +397,13 @@ connection_setting_add (const GPtrArray *block,
 	                      prefix ? prefix : "",
 	                      iface);
 
-	uuid = nm_utils_uuid_generate_from_strings ("ibft",
-	                                            s_hwaddr,
-	                                            s_vlanid ? "V" : "v",
-	                                            s_vlanid ? s_vlanid : "",
-	                                            s_ip4addr ? "A" : "DHCP",
-	                                            s_ip4addr ? s_ip4addr : "",
-	                                            NULL);
+	uuid = _nm_utils_uuid_generate_from_strings ("ibft",
+	                                             s_hwaddr,
+	                                             s_vlanid ? "V" : "v",
+	                                             s_vlanid ? s_vlanid : "",
+	                                             s_ip4addr ? "A" : "DHCP",
+	                                             s_ip4addr ? s_ip4addr : "",
+	                                             NULL);
 
 	s_con = nm_setting_connection_new ();
 	g_object_set (s_con,
@@ -439,7 +431,7 @@ is_ibft_vlan_device (const GPtrArray *block)
 		/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it
 		 * means "no VLAN".
 		 */
-		if (nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1)
+		if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1)
 			return TRUE;
 	}
 	return FALSE;
@@ -466,7 +458,7 @@ vlan_setting_add_from_block (const GPtrArray *block,
 	g_assert (vlan_id_str);
 
 	/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */
-	vlan_id = nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1);
+	vlan_id = _nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1);
 	if (vlan_id == -1) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid VLAN_ID '%s'", vlan_id_str);
diff --git a/src/settings/plugins/ibft/tests/Makefile.in b/src/settings/plugins/ibft/tests/Makefile.in
index d682318a..266497b6 100644
--- a/src/settings/plugins/ibft/tests/Makefile.in
+++ b/src/settings/plugins/ibft/tests/Makefile.in
@@ -205,13 +205,196 @@ am__tty_colors = { \
     std=''; \
   fi; \
 }
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 am__DIST_COMMON = $(srcdir)/Makefile.in \
-	$(top_srcdir)/build-aux/depcomp
+	$(top_srcdir)/build-aux/depcomp \
+	$(top_srcdir)/build-aux/test-driver
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -325,6 +508,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
@@ -510,7 +694,7 @@ EXTRA_DIST = \
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -656,98 +840,168 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
 	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	elif test -n "$$redo_logs"; then \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
 	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
 	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
 	    else \
-	      skipped="($$skip tests were not run)"; \
+	      color_start= color_end=; \
 	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test-ibft.log: test-ibft$(EXEEXT)
+	@p='test-ibft$(EXEEXT)'; \
+	b='test-ibft'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
 
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -804,6 +1058,9 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
@@ -899,7 +1156,7 @@ uninstall-am:
 	installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags tags-am uninstall uninstall-am
+	recheck tags tags-am uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
diff --git a/src/settings/plugins/ifcfg-rh/Makefile.in b/src/settings/plugins/ifcfg-rh/Makefile.in
index 89511983..0cd26960 100644
--- a/src/settings/plugins/ifcfg-rh/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/Makefile.in
@@ -269,6 +269,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -382,6 +383,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
index 94d22258..a18920c6 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
@@ -96,7 +96,7 @@ devtimeout_ready (gpointer user_data)
 }
 
 static void
-link_changed (NMPlatform *platform, int ifindex, NMPlatformLink *link,
+link_changed (NMPlatform *platform, NMPObjectType *obj_type, int ifindex, const NMPlatformLink *link,
               NMPlatformSignalChangeType change_type, NMPlatformReason reason,
               NMConnection *self)
 {
@@ -164,7 +164,7 @@ nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
 	if (!devtimeout)
 		return;
 
-	if (nm_platform_link_get_ifindex (ifname) != 0)
+	if (nm_platform_link_get_ifindex (NM_PLATFORM_GET, ifname) != 0)
 		return;
 
 	/* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */
@@ -201,7 +201,8 @@ files_changed_cb (NMInotifyHelper *ih,
 NMIfcfgConnection *
 nm_ifcfg_connection_new (NMConnection *source,
                          const char *full_path,
-                         GError **error)
+                         GError **error,
+                         gboolean *out_ignore_error)
 {
 	GObject *object;
 	NMConnection *tmp;
@@ -211,13 +212,17 @@ nm_ifcfg_connection_new (NMConnection *source,
 
 	g_assert (source || full_path);
 
+	if (out_ignore_error)
+		*out_ignore_error = FALSE;
+
 	/* If we're given a connection already, prefer that instead of re-reading */
 	if (source)
 		tmp = g_object_ref (source);
 	else {
 		tmp = connection_from_file (full_path,
 		                            &unhandled_spec,
-		                            error);
+		                            error,
+		                            out_ignore_error);
 		if (!tmp)
 			return NULL;
 
@@ -376,7 +381,7 @@ commit_changes (NMSettingsConnection *connection,
 	 */
 	filename = nm_settings_connection_get_filename (connection);
 	if (filename) {
-		reread = connection_from_file (filename, NULL, NULL);
+		reread = connection_from_file (filename, NULL, NULL, NULL);
 		if (reread) {
 			same = nm_connection_compare (NM_CONNECTION (connection),
 			                              reread,
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h
index 328e58f5..44e02987 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h
@@ -48,7 +48,8 @@ GType nm_ifcfg_connection_get_type (void);
 
 NMIfcfgConnection *nm_ifcfg_connection_new (NMConnection *source,
                                             const char *full_path,
-                                            GError **error);
+                                            GError **error,
+                                            gboolean *out_ignore_error);
 
 const char *nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self);
 const char *nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self);
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index b7efa864..1a3f791a 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -169,7 +169,8 @@ remove_connection (SCPluginIfcfg *self, NMIfcfgConnection *connection)
 
 	g_object_ref (connection);
 	g_hash_table_remove (priv->connections, nm_connection_get_uuid (NM_CONNECTION (connection)));
-	nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
+	if (!unmanaged && !unrecognized)
+		nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
 	g_object_unref (connection);
 
 	/* Emit changes _after_ removing the connection */
@@ -213,6 +214,7 @@ update_connection (SCPluginIfcfg *self,
 	const char *new_unrecognized = NULL, *old_unrecognized = NULL;
 	gboolean unmanaged_changed = FALSE, unrecognized_changed = FALSE;
 	const char *uuid;
+	gboolean ignore_error = FALSE;
 
 	g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL);
 	g_return_val_if_fail (full_path || source, NULL);
@@ -222,13 +224,16 @@ update_connection (SCPluginIfcfg *self,
 
 	/* Create a NMIfcfgConnection instance, either by reading from @full_path or
 	 * based on @source. */
-	connection_new = nm_ifcfg_connection_new (source, full_path, error);
+	connection_new = nm_ifcfg_connection_new (source, full_path, &local, &ignore_error);
 	if (!connection_new) {
 		/* Unexpected failure. Probably the file is invalid? */
 		if (   connection
 		    && !protect_existing_connection
 		    && (!protected_connections || !g_hash_table_contains (protected_connections, connection)))
 			remove_connection (self, connection);
+		if (!source && !ignore_error)
+			_LOGW ("loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)");
+		g_propagate_error (error, local);
 		return NULL;
 	}
 
@@ -421,25 +426,13 @@ ifcfg_dir_changed (GFileMonitor *monitor,
                    gpointer user_data)
 {
 	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
-	char *path, *base, *ifcfg_path;
+	char *path, *ifcfg_path;
 	NMIfcfgConnection *connection;
 
 	path = g_file_get_path (file);
-	if (utils_should_ignore_file (path, FALSE)) {
-		g_free (path);
-		return;
-	}
-
-	_LOGD ("ifcfg_dir_changed(%s) = %d", path, event_type);
 
-	base = g_file_get_basename (file);
-	if (utils_is_ifcfg_alias_file (base, NULL)) {
-		/* Alias file changed. Get the base ifcfg file from it */
-		ifcfg_path = utils_get_ifcfg_from_alias (path);
-	} else {
-		/* Given any ifcfg, keys, or routes file, get the ifcfg file path */
-		ifcfg_path = utils_get_ifcfg_path (path);
-	}
+	ifcfg_path = utils_detect_ifcfg_path (path, FALSE);
+	_LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ? ifcfg_path : "(none)");
 	if (ifcfg_path) {
 		connection = find_by_path (plugin, ifcfg_path);
 		switch (event_type) {
@@ -458,7 +451,6 @@ ifcfg_dir_changed (GFileMonitor *monitor,
 		g_free (ifcfg_path);
 	}
 	g_free (path);
-	g_free (base);
 }
 
 static void
@@ -542,18 +534,14 @@ read_connections (SCPluginIfcfg *plugin)
 
 	filenames = g_ptr_array_new_with_free_func (g_free);
 	while ((item = g_dir_read_name (dir))) {
-		char *full_path;
-
-		if (utils_should_ignore_file (item, TRUE))
-			continue;
-		if (utils_is_ifcfg_alias_file (item, NULL))
-			continue;
+		char *full_path, *real_path;
 
 		full_path = g_build_filename (IFCFG_DIR, item, NULL);
-		if (!utils_get_ifcfg_name (full_path, TRUE))
-			g_free (full_path);
-		else
-			g_ptr_array_add (filenames, full_path);
+		real_path = utils_detect_ifcfg_path (full_path, TRUE);
+
+		if (real_path)
+			g_ptr_array_add (filenames, real_path);
+		g_free (full_path);
 	}
 	g_dir_close (dir);
 
@@ -625,20 +613,25 @@ load_connection (NMSystemConfigInterface *config,
 	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config);
 	NMIfcfgConnection *connection;
 	int dir_len = strlen (IFCFG_DIR);
+	char *ifcfg_path;
 
 	if (   strncmp (filename, IFCFG_DIR, dir_len) != 0
 	    || filename[dir_len] != '/'
 	    || strchr (filename + dir_len + 1, '/') != NULL)
 		return FALSE;
 
-	if (utils_should_ignore_file (filename + dir_len + 1, TRUE))
+	/* get the real ifcfg-path. This allows us to properly
+	 * handle load command using a route-* file etc. */
+	ifcfg_path = utils_detect_ifcfg_path (filename, FALSE);
+	if (!ifcfg_path)
 		return FALSE;
 
-	connection = find_by_path (plugin, filename);
-	update_connection (plugin, NULL, filename, connection, TRUE, NULL, NULL);
+	connection = find_by_path (plugin, ifcfg_path);
+	update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL);
 	if (!connection)
-		connection = find_by_path (plugin, filename);
+		connection = find_by_path (plugin, ifcfg_path);
 
+	g_free (ifcfg_path);
 	return (connection != NULL);
 }
 
@@ -1139,7 +1132,7 @@ nm_system_config_factory (void)
 			dbus_g_connection_register_g_object (priv->bus,
 			                                     DBUS_OBJECT_PATH,
 			                                     G_OBJECT (singleton));
-		_LOGI ("Acquired D-Bus service %s", DBUS_SERVICE_NAME);
+		_LOGD ("Acquired D-Bus service %s", DBUS_SERVICE_NAME);
 	} else
 		g_object_ref (singleton);
 
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index 41439f61..ec874167 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -53,7 +53,6 @@
 #include <nm-utils.h>
 
 #include "nm-platform.h"
-#include "nm-posix-signals.h"
 #include "NetworkManagerUtils.h"
 #include "nm-logging.h"
 #include "gsystem-local-alloc.h"
@@ -178,6 +177,8 @@ make_connection_setting (const char *file,
 	                                      NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
 	                                      NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
 	                                      NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT),
+	              NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
+	              svTrueValue (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT),
 	              NULL);
 
 	value = svGetValue (ifcfg, "USERS", FALSE);
@@ -507,7 +508,7 @@ read_one_ip4_route (shvarFile *ifcfg,
 	/* Metric */
 	value = svGetValue (ifcfg, metric_tag, FALSE);
 	if (value) {
-		metric = nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
+		metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
 		if (metric < 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid IP4 route metric '%s'", value);
@@ -1153,16 +1154,14 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename, const char *networ
 	GDir *dir;
 	char *dirname, *base;
 	shvarFile *parsed;
-	NMIPAddress *base_addr;
+	NMIPAddress *base_addr = NULL;
 	GError *err = NULL;
 
 	g_return_if_fail (s_ip4 != NULL);
 	g_return_if_fail (filename != NULL);
 
-	if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0)
-		return;
-
-	base_addr = nm_setting_ip_config_get_address (s_ip4, 0);
+	if (nm_setting_ip_config_get_num_addresses (s_ip4) > 0)
+		base_addr = nm_setting_ip_config_get_address (s_ip4, 0);
 
 	dirname = g_path_get_dirname (filename);
 	g_return_if_fail (dirname != NULL);
@@ -3351,7 +3350,7 @@ make_wireless_setting (shvarFile *ifcfg,
 	value = svGetValue (ifcfg, "CHANNEL", FALSE);
 	if (value) {
 		errno = 0;
-		chan = nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
+		chan = _nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
 		if (errno || (chan == 0)) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid wireless channel '%s'", value);
@@ -4335,11 +4334,11 @@ is_wifi_device (const char *name, shvarFile *parsed)
 	g_return_val_if_fail (name != NULL, FALSE);
 	g_return_val_if_fail (parsed != NULL, FALSE);
 
-	ifindex = nm_platform_link_get_ifindex (name);
+	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, name);
 	if (ifindex == 0)
 		return FALSE;
 
-	return nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_WIFI;
+	return nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_WIFI;
 }
 
 static void
@@ -4433,13 +4432,10 @@ make_vlan_setting (shvarFile *ifcfg,
 			/* Grab VLAN ID from interface name; this takes precedence over the
 			 * separate VLAN_ID property for backwards compat.
 			 */
-			vlan_id = (gint) g_ascii_strtoll (p, &end, 10);
-			if (vlan_id < 0 || vlan_id > 4095 || end == p || *end) {
-				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-				             "Failed to determine VLAN ID from DEVICE '%s'",
-				             iface_name);
-				goto error;
-			}
+
+			gint device_vlan_id = (gint) g_ascii_strtoll (p, &end, 10);
+			if (device_vlan_id >= 0 && device_vlan_id <= 4095 && end != p && !*end)
+				vlan_id = device_vlan_id;
 		}
 	}
 
@@ -4679,7 +4675,8 @@ connection_from_file_full (const char *filename,
 
 		connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
 		if (!connection)
-			PARSE_WARNING ("NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
+			             "NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
 		goto done;
 	}
 
@@ -4841,18 +4838,13 @@ done:
 NMConnection *
 connection_from_file (const char *filename,
                       char **out_unhandled,
-                      GError **error)
+                      GError **error,
+                      gboolean *out_ignore_error)
 {
-	gboolean ignore_error = FALSE;
-	NMConnection *conn;
-
-	conn = connection_from_file_full (filename, NULL, NULL,
+	return connection_from_file_full (filename, NULL, NULL,
 	                                  out_unhandled,
 	                                  error,
-	                                  &ignore_error);
-	if (error && *error && !ignore_error)
-		PARSE_WARNING ("%s", (*error)->message);
-	return conn;
+	                                  out_ignore_error);
 }
 
 NMConnection *
@@ -4885,7 +4877,7 @@ devtimeout_from_file (const char *filename)
 
 	devtimeout_str = svGetValue (ifcfg, "DEVTIMEOUT", FALSE);
 	if (devtimeout_str) {
-		devtimeout = nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
+		devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
 		g_free (devtimeout_str);
 	} else
 		devtimeout = 0;
diff --git a/src/settings/plugins/ifcfg-rh/reader.h b/src/settings/plugins/ifcfg-rh/reader.h
index 70e9ce4e..2096ffc4 100644
--- a/src/settings/plugins/ifcfg-rh/reader.h
+++ b/src/settings/plugins/ifcfg-rh/reader.h
@@ -28,7 +28,8 @@
 
 NMConnection *connection_from_file (const char *filename,
                                     char **out_unhandled,
-                                    GError **error);
+                                    GError **error,
+                                    gboolean *out_ignore_error);
 
 char *uuid_from_file (const char *filename);
 
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 5f3d173b..4a5ca1d1 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -36,7 +36,8 @@
 
 #include "shvar.h"
 
-#include "NetworkManagerUtils.h"
+#include "gsystem-local-alloc.h"
+#include "nm-core-internal.h"
 #include "nm-logging.h"
 
 #define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, "    " msg)
@@ -206,8 +207,8 @@ static const char escapees[] = "\"'\\$~`";		/* must be escaped */
 static const char spaces[] = " \t|&;()<>";		/* only require "" */
 static const char newlines[] = "\n\r";			/* will be removed */
 
-char *
-svEscape (const char *s)
+const char *
+svEscape (const char *s, char **to_free)
 {
 	char *new;
 	int i, j, mangle = 0, space = 0, newline = 0;
@@ -223,8 +224,10 @@ svEscape (const char *s)
 		if (strchr (newlines, s[i]))
 			newline++;
 	}
-	if (!mangle && !space && !newline)
-		return strdup (s);
+	if (!mangle && !space && !newline) {
+		*to_free = NULL;
+		return s;
+	}
 
 	newlen = slen + mangle - newline + 3;	/* 3 is extra ""\0 */
 	new = g_malloc (newlen);
@@ -243,6 +246,7 @@ svEscape (const char *s)
 	new[j++] = '\0';
 	g_assert (j == slen + mangle - newline + 3);
 
+	*to_free = new;
 	return new;
 }
 
@@ -253,6 +257,22 @@ svEscape (const char *s)
 char *
 svGetValue (shvarFile *s, const char *key, gboolean verbatim)
 {
+	char *value;
+
+	value = svGetValueFull (s, key, verbatim);
+	if (value && !*value) {
+		g_free (value);
+		return NULL;
+	}
+	return value;
+}
+
+/* svGetValueFull() is identical to svGetValue() except that
+ * svGetValue() will never return an empty value (but %NULL instead).
+ * svGetValueFull() will return empty values if that is the value for the @key. */
+char *
+svGetValueFull (shvarFile *s, const char *key, gboolean verbatim)
+{
 	char *value = NULL;
 	char *line;
 	char *keyString;
@@ -276,12 +296,7 @@ svGetValue (shvarFile *s, const char *key, gboolean verbatim)
 	}
 	g_free (keyString);
 
-	if (value && value[0]) {
-		return value;
-	} else {
-		g_free (value);
-		return NULL;
-	}
+	return value;
 }
 
 /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
@@ -321,7 +336,7 @@ svTrueValue (shvarFile *s, const char *key, gboolean def)
  * @max: the maximum for range-check
  * @fallback: the fallback value in any error case
  *
- * Reads a value @key and converts it to an integer using nm_utils_ascii_str_to_int64().
+ * Reads a value @key and converts it to an integer using _nm_utils_ascii_str_to_int64().
  * In case of error, @errno will be set and @fallback returned. */
 gint64
 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback)
@@ -330,13 +345,13 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
 	gint64 result;
 	int errsv;
 
-	tmp = svGetValue (s, key, FALSE);
+	tmp = svGetValueFull (s, key, FALSE);
 	if (!tmp) {
 		errno = 0;
 		return fallback;
 	}
 
-	result = nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback);
+	result = _nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback);
 	errsv = errno;
 	if (errsv != 0)
 		PARSE_WARNING ("Error reading '%s' value '%s' as integer (%d)", key, tmp, errsv);
@@ -354,20 +369,30 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
 void
 svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim)
 {
-	char *newval = NULL, *oldval = NULL;
+	svSetValueFull (s, key, value && value[0] ? value : NULL, verbatim);
+}
+
+/* Same as svSetValue() but it preserves empty @value -- contrary to
+ * svSetValue() for which "" effectively means to remove the value. */
+void
+svSetValueFull (shvarFile *s, const char *key, const char *value, gboolean verbatim)
+{
+	gs_free char *newval_free = NULL;
+	gs_free char *oldval = NULL;
+	const char *newval;
 	char *keyValue;
 
 	g_return_if_fail (s != NULL);
 	g_return_if_fail (key != NULL);
 	/* value may be NULL */
 
-	if (value)
-		newval = verbatim ? g_strdup (value) : svEscape (value);
-	keyValue = g_strdup_printf ("%s=%s", key, newval ? newval : "");
-
-	oldval = svGetValue (s, key, FALSE);
+	if (!value || verbatim)
+		newval = value;
+	else
+		newval = svEscape (value, &newval_free);
+	oldval = svGetValueFull (s, key, FALSE);
 
-	if (!newval || !newval[0]) {
+	if (!newval) {
 		/* delete value */
 		if (oldval) {
 			/* delete line */
@@ -376,15 +401,15 @@ svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim)
 			g_list_free_1 (s->current);
 			s->modified = TRUE;
 		}
-		g_free (keyValue);
-		goto end;
+		return;
 	}
 
+	keyValue = g_strdup_printf ("%s=%s", key, newval);
 	if (!oldval) {
 		/* append line */
 		s->lineList = g_list_append (s->lineList, keyValue);
 		s->modified = TRUE;
-		goto end;
+		return;
 	}
 
 	if (strcmp (oldval, newval) != 0) {
@@ -397,11 +422,6 @@ svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim)
 		s->modified = TRUE;
 	} else
 		g_free (keyValue);
-
- end:
-	g_free (newval);
-	g_free (oldval);
-	return;
 }
 
 /* Write the current contents iff modified.  Returns FALSE on error
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index b2a2f263..4902541b 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -56,6 +56,7 @@ shvarFile *svOpenFile (const char *name, GError **error);
  * be freed by the caller.
  */
 char *svGetValue (shvarFile *s, const char *key, gboolean verbatim);
+char *svGetValueFull (shvarFile *s, const char *key, gboolean verbatim);
 
 /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
  * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
@@ -71,6 +72,7 @@ gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, g
  * to the top of the file.
  */
 void svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim);
+void svSetValueFull (shvarFile *s, const char *key, const char *value, gboolean verbatim);
 
 
 /* Write the current contents iff modified.  Returns FALSE on error
@@ -84,8 +86,8 @@ gboolean svWriteFile (shvarFile *s, int mode, GError **error);
 /* Close the file descriptor (if open) and free the shvarFile. */
 void svCloseFile (shvarFile *s);
 
-/* Return a new escaped string */
-char *svEscape (const char *s);
+/* Return @s unmodified or an escaped string */
+const char *svEscape (const char *s, char **to_free);
 
 /* Unescape a string in-place */
 void svUnescape (char *s);
diff --git a/src/settings/plugins/ifcfg-rh/tests/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/Makefile.in
index d3c838a7..d0832e47 100644
--- a/src/settings/plugins/ifcfg-rh/tests/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/Makefile.in
@@ -192,7 +192,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-	distdir
+	check recheck distdir
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
 # Read a list of newline-separated strings from the standard input,
 # and print each of them once, without duplicates.  Input order is
@@ -234,9 +234,190 @@ am__tty_colors = { \
     std=''; \
   fi; \
 }
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 DIST_SUBDIRS = network-scripts
 am__DIST_COMMON = $(srcdir)/Makefile.in \
-	$(top_srcdir)/build-aux/depcomp
+	$(top_srcdir)/build-aux/depcomp \
+	$(top_srcdir)/build-aux/test-driver
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -267,6 +448,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -380,6 +562,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
@@ -568,7 +751,7 @@ with_valgrind = @with_valgrind@
 all: all-recursive
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -811,98 +994,175 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
 	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	elif test -n "$$redo_logs"; then \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
 	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
 	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
 	    else \
-	      skipped="($$skip tests were not run)"; \
+	      color_start= color_end=; \
 	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test-ifcfg-rh-utils.log: test-ifcfg-rh-utils$(EXEEXT)
+	@p='test-ifcfg-rh-utils$(EXEEXT)'; \
+	b='test-ifcfg-rh-utils'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test-ifcfg-rh.log: test-ifcfg-rh$(EXEEXT)
+	@p='test-ifcfg-rh$(EXEEXT)'; \
+	b='test-ifcfg-rh'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
 
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -985,6 +1245,9 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
@@ -1080,8 +1343,8 @@ uninstall-am:
 	install-strip installcheck installcheck-am installdirs \
 	installdirs-am maintainer-clean maintainer-clean-generic \
 	mostlyclean mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
-	uninstall-am
+	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+	uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
index 58798f77..ef06c5ef 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
@@ -135,6 +135,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -248,6 +249,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c
index 13f1126e..68f236af 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c
@@ -115,15 +115,9 @@ test_ignored (const char *desc, const char *path, gboolean expected_ignored)
 	ASSERT (result == expected_ignored, desc, "unexpected ignore result for path '%s'", path);
 }
 
-NMTST_DEFINE ();
-
-int main (int argc, char **argv)
+static void
+test_name (void)
 {
-	char *base;
-
-	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
-
-	/* The tests */
 	test_get_ifcfg_name ("get-ifcfg-name-bad", "/foo/bar/adfasdfadf", FALSE, NULL);
 	test_get_ifcfg_name ("get-ifcfg-name-good", "/foo/bar/ifcfg-FooBar", FALSE, "FooBar");
 	test_get_ifcfg_name ("get-ifcfg-name-keys", "/foo/bar/keys-BlahLbah", FALSE, "BlahLbah");
@@ -137,7 +131,11 @@ int main (int argc, char **argv)
 	test_get_ifcfg_name ("get-ifcfg-name-bad2-ifcfg", "/foo/bar/asdfasifcfg-Foobar", FALSE, NULL);
 	test_get_ifcfg_name ("get-ifcfg-name-bad2-keys", "/foo/bar/asdfaskeys-Foobar", FALSE, NULL);
 	test_get_ifcfg_name ("get-ifcfg-name-bad2-route", "/foo/bar/asdfasroute-Foobar", FALSE, NULL);
+}
 
+static void
+test_path (void)
+{
 	test_get_ifcfg_path ("ifcfg-path-bad", "/foo/bar/adfasdfasdf", NULL);
 	test_get_ifcfg_path ("ifcfg-path-from-keys-no-path", "keys-BlahBlah", "ifcfg-BlahBlah");
 	test_get_ifcfg_path ("ifcfg-path-from-keys", "/foo/bar/keys-BlahBlah", "/foo/bar/ifcfg-BlahBlah");
@@ -152,7 +150,11 @@ int main (int argc, char **argv)
 	test_get_route_path ("route-path-from-ifcfg-no-path", "ifcfg-FooBar", "route-FooBar");
 	test_get_route_path ("route-path-from-ifcfg", "/foo/bar/ifcfg-FooBar", "/foo/bar/route-FooBar");
 	test_get_route_path ("route-path-from-keys", "/foo/bar/keys-FooBar", "/foo/bar/route-FooBar");
+}
 
+static void
+test_ignore (void)
+{
 	test_ignored ("ignored-ifcfg", "ifcfg-FooBar", FALSE);
 	test_ignored ("ignored-keys", "keys-FooBar", FALSE);
 	test_ignored ("ignored-route", "route-FooBar", FALSE);
@@ -163,10 +165,19 @@ int main (int argc, char **argv)
 	test_ignored ("ignored-rpmnew", "ifcfg-FooBar" RPMNEW_TAG, TRUE);
 	test_ignored ("ignored-augnew", "ifcfg-FooBar" AUGNEW_TAG, TRUE);
 	test_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE);
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
+
+	/* The tests */
+	g_test_add_func ("/settings/plugins/ifcfg-rh/name", test_name);
+	g_test_add_func ("/settings/plugins/ifcfg-rh/path", test_path);
+	g_test_add_func ("/settings/plugins/ifcfg-rh/ignore", test_ignore);
 
-	base = g_path_get_basename (argv[0]);
-	fprintf (stdout, "%s: SUCCESS\n", base);
-	g_free (base);
-	return 0;
+	return g_test_run ();
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/utils.c b/src/settings/plugins/ifcfg-rh/utils.c
index 4b7cb43d..a793288d 100644
--- a/src/settings/plugins/ifcfg-rh/utils.c
+++ b/src/settings/plugins/ifcfg-rh/utils.c
@@ -25,6 +25,9 @@
 #include <string.h>
 
 #include "nm-core-internal.h"
+#include "nm-macros-internal.h"
+#include "NetworkManagerUtils.h"
+#include "gsystem-local-alloc.h"
 
 #include "utils.h"
 #include "shvar.h"
@@ -149,41 +152,34 @@ check_suffix (const char *base, const char *tag)
 gboolean
 utils_should_ignore_file (const char *filename, gboolean only_ifcfg)
 {
-	char *base;
-	gboolean ignore = TRUE;
-	gboolean is_ifcfg = FALSE;
-	gboolean is_other = FALSE;
+	gs_free char *base = NULL;
 
 	g_return_val_if_fail (filename != NULL, TRUE);
 
 	base = g_path_get_basename (filename);
-	g_return_val_if_fail (base != NULL, TRUE);
 
 	/* Only handle ifcfg, keys, and routes files */
-	if (!strncmp (base, IFCFG_TAG, strlen (IFCFG_TAG)))
-		is_ifcfg = TRUE;
-
-	if (only_ifcfg == FALSE) {
-		if (   !strncmp (base, KEYS_TAG, strlen (KEYS_TAG))
-		    || !strncmp (base, ROUTE_TAG, strlen (ROUTE_TAG))
-		    || !strncmp (base, ROUTE6_TAG, strlen (ROUTE6_TAG)))
-				is_other = TRUE;
+	if (strncmp (base, IFCFG_TAG, strlen (IFCFG_TAG)) != 0) {
+		if (only_ifcfg)
+			return TRUE;
+		else if (   strncmp (base, KEYS_TAG, strlen (KEYS_TAG)) != 0
+		         && strncmp (base, ROUTE_TAG, strlen (ROUTE_TAG)) != 0
+		         && strncmp (base, ROUTE6_TAG, strlen (ROUTE6_TAG)) != 0)
+			return TRUE;
 	}
 
 	/* But not those that have certain suffixes */
-	if (   (is_ifcfg || is_other)
-	    && !check_suffix (base, BAK_TAG)
-	    && !check_suffix (base, TILDE_TAG)
-	    && !check_suffix (base, ORIG_TAG)
-	    && !check_suffix (base, REJ_TAG)
-	    && !check_suffix (base, RPMNEW_TAG)
-	    && !check_suffix (base, AUGNEW_TAG)
-	    && !check_suffix (base, AUGTMP_TAG)
-	    && !check_rpm_temp_suffix (base))
-		ignore = FALSE;
-
-	g_free (base);
-	return ignore;
+	if (   check_suffix (base, BAK_TAG)
+	    || check_suffix (base, TILDE_TAG)
+	    || check_suffix (base, ORIG_TAG)
+	    || check_suffix (base, REJ_TAG)
+	    || check_suffix (base, RPMNEW_TAG)
+	    || check_suffix (base, AUGNEW_TAG)
+	    || check_suffix (base, AUGTMP_TAG)
+	    || check_rpm_temp_suffix (base))
+		return TRUE;
+
+	return FALSE;
 }
 
 char *
@@ -205,34 +201,43 @@ utils_cert_path (const char *parent, const char *suffix)
 const char *
 utils_get_ifcfg_name (const char *file, gboolean only_ifcfg)
 {
-	const char *name = NULL, *start = NULL;
-	char *base;
+	const char *name;
 
 	g_return_val_if_fail (file != NULL, NULL);
 
-	base = g_path_get_basename (file);
-	if (!base)
+	name = strrchr (file, '/');
+	if (!name)
+		name = file;
+	else
+		name++;
+	if (!*name)
 		return NULL;
 
-	/* Find the point in 'file' where 'base' starts.  We use 'file' since it's
-	 * const and thus will survive after we free 'base'.
-	 */
-	start = file + strlen (file) - strlen (base);
-	g_assert (strcmp (start, base) == 0);
-	g_free (base);
-
-	if (!strncmp (start, IFCFG_TAG, strlen (IFCFG_TAG)))
-		name = start + strlen (IFCFG_TAG);
-	else if (only_ifcfg == FALSE)  {
-		if (!strncmp (start, KEYS_TAG, strlen (KEYS_TAG)))
-			name = start + strlen (KEYS_TAG);
-		else if (!strncmp (start, ROUTE_TAG, strlen (ROUTE_TAG)))
-			name = start + strlen (ROUTE_TAG);
-		else if (!strncmp (start, ROUTE6_TAG, strlen (ROUTE6_TAG)))
-			name = start + strlen (ROUTE6_TAG);
+#define MATCH_TAG_AND_RETURN(name, TAG) \
+	G_STMT_START { \
+		if (strncmp (name, TAG, STRLEN (TAG)) == 0) { \
+			name += STRLEN (TAG); \
+			if (name[0] == '\0') \
+				return NULL; \
+			else \
+				return name; \
+		} \
+	} G_STMT_END
+
+	/* Do not detect alias files and return 'eth0:0' instead of 'eth0'.
+	 * Unfortunately, we cannot be sure that our files don't contain colons,
+	 * so we cannot reject files with colons.
+	 *
+	 * Instead, you must not call utils_get_ifcfg_name() with an alias file
+	 * or files that are ignored. */
+	MATCH_TAG_AND_RETURN (name, IFCFG_TAG);
+	if (!only_ifcfg) {
+		MATCH_TAG_AND_RETURN (name, KEYS_TAG);
+		MATCH_TAG_AND_RETURN (name, ROUTE_TAG);
+		MATCH_TAG_AND_RETURN (name, ROUTE6_TAG);
 	}
 
-	return name;
+	return NULL;
 }
 
 /* Used to get any ifcfg/extra file path from any other ifcfg/extra path
@@ -420,26 +425,43 @@ utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg)
 }
 
 char *
-utils_get_ifcfg_from_alias (const char *alias)
+utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg)
 {
-	char *base, *ptr, *ifcfg = NULL;
+	gs_free char *base = NULL;
+	char *ptr, *ifcfg = NULL;
 
-	g_return_val_if_fail (alias != NULL, NULL);
+	g_return_val_if_fail (path != NULL, NULL);
 
-	base = g_path_get_basename (alias);
-	g_return_val_if_fail (base != NULL, NULL);
+	if (utils_should_ignore_file (path, only_ifcfg))
+		return NULL;
 
-	if (utils_is_ifcfg_alias_file (base, NULL)) {
-		ifcfg = g_strdup (alias);
-		ptr = strrchr (ifcfg, ':');
-		if (ptr)
-			*ptr = '\0';
-		else {
+	base = g_path_get_basename (path);
+
+	if (strncmp (base, IFCFG_TAG, STRLEN (IFCFG_TAG)) == 0) {
+		if (base[STRLEN (IFCFG_TAG)] == '\0')
+			return NULL;
+		if (utils_is_ifcfg_alias_file (base, NULL)) {
+			ifcfg = g_strdup (path);
+			ptr = strrchr (ifcfg, ':');
+			if (ptr && ptr > ifcfg) {
+				*ptr = '\0';
+				if (g_file_test (ifcfg, G_FILE_TEST_EXISTS)) {
+					/* the file has a colon, so it is probably an alias.
+					 * To be ~more~ certain that this is an alias file,
+					 * check whether a corresponding base file exists. */
+					if (only_ifcfg) {
+						g_free (ifcfg);
+						return NULL;
+					}
+					return ifcfg;
+				}
+			}
 			g_free (ifcfg);
-			ifcfg = NULL;
 		}
+		return g_strdup (path);
 	}
 
-	g_free (base);
-	return ifcfg;
+	if (only_ifcfg)
+		return NULL;
+	return utils_get_ifcfg_path (path);
 }
diff --git a/src/settings/plugins/ifcfg-rh/utils.h b/src/settings/plugins/ifcfg-rh/utils.h
index 445437c4..547bfcb2 100644
--- a/src/settings/plugins/ifcfg-rh/utils.h
+++ b/src/settings/plugins/ifcfg-rh/utils.h
@@ -59,7 +59,8 @@ gboolean utils_has_complex_routes (const char *filename);
 gboolean utils_ignore_ip_config (NMConnection *connection);
 
 gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg);
-char *utils_get_ifcfg_from_alias (const char *alias);
+
+char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg);
 
 #endif  /* _UTILS_H_ */
 
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index d2b0c970..52bf51d1 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -888,20 +888,22 @@ write_wireless_setting (NMConnection *connection,
 		svSetValue (ifcfg, "ESSID", str->str, TRUE);
 		g_string_free (str, TRUE);
 	} else {
+		const char *tmp_escaped;
+
 		/* Printable SSIDs always get quoted */
 		memset (buf, 0, sizeof (buf));
 		memcpy (buf, ssid_data, ssid_len);
-		tmp = svEscape (buf);
+		tmp_escaped = svEscape (buf, &tmp);
 
 		/* svEscape will usually quote the string, but just for consistency,
 		 * if svEscape doesn't quote the ESSID, we quote it ourselves.
 		 */
-		if (tmp[0] != '"' && tmp[strlen (tmp) - 1] != '"') {
-			tmp2 = g_strdup_printf ("\"%s\"", tmp);
+		if (tmp_escaped[0] != '"' && tmp_escaped[strlen (tmp_escaped) - 1] != '"') {
+			tmp2 = g_strdup_printf ("\"%s\"", tmp_escaped);
 			svSetValue (ifcfg, "ESSID", tmp2, TRUE);
 			g_free (tmp2);
 		} else
-			svSetValue (ifcfg, "ESSID", tmp, TRUE);
+			svSetValue (ifcfg, "ESSID", tmp_escaped, TRUE);
 		g_free (tmp);
 	}
 
@@ -1665,7 +1667,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 {
 	guint32 n, i;
 	GString *str;
-	const char *master;
+	const char *master, *type;
 	char *tmp;
 	gint i_int;
 
@@ -1682,6 +1684,20 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	svSetValue (ifcfg, "AUTOCONNECT_PRIORITY", tmp, FALSE);
 	g_free (tmp);
 
+	/* Only save the value for master connections */
+	svSetValue (ifcfg, "AUTOCONNECT_SLAVES", NULL, FALSE);
+	type = nm_setting_connection_get_connection_type (s_con);
+	if (   !g_strcmp0 (type, NM_SETTING_BOND_SETTING_NAME)
+	    || !g_strcmp0 (type, NM_SETTING_TEAM_SETTING_NAME)
+	    || !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
+		NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
+		autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
+		svSetValue (ifcfg, "AUTOCONNECT_SLAVES",
+		            autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
+		            autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL,
+		            FALSE);
+	}
+
 	/* Permissions */
 	svSetValue (ifcfg, "USERS", NULL, FALSE);
 	n = nm_setting_connection_get_num_permissions (s_con);
@@ -2448,7 +2464,7 @@ escape_id (const char *id)
 	while (*p) {
 		if (*p == ' ')
 			*p = '_';
-		else if (strchr ("\\][|/=()!", *p))
+		else if (strchr ("\\][|/=()!:", *p))
 			*p = '-';
 		p++;
 	}
diff --git a/src/settings/plugins/ifcfg-suse/Makefile.in b/src/settings/plugins/ifcfg-suse/Makefile.in
index f7df30b4..3d7df19e 100644
--- a/src/settings/plugins/ifcfg-suse/Makefile.in
+++ b/src/settings/plugins/ifcfg-suse/Makefile.in
@@ -219,6 +219,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -332,6 +333,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ifnet/Makefile.in b/src/settings/plugins/ifnet/Makefile.in
index cbedcd79..4832249a 100644
--- a/src/settings/plugins/ifnet/Makefile.in
+++ b/src/settings/plugins/ifnet/Makefile.in
@@ -267,6 +267,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -380,6 +381,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index 41baf315..888ee589 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -706,13 +706,13 @@ make_ip4_setting (NMConnection *connection,
 		GError *local = NULL;
 
 		if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) {
-			metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+			metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
 		} else {
 			metric_str = ifnet_get_global_data ("metric");
 			if (metric_str) {
 				stripped = g_strdup (metric_str);
 				strip_string (stripped, '"');
-				metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+				metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
 				g_free (stripped);
 			} else
 				metric = -1;
@@ -846,13 +846,13 @@ make_ip6_setting (NMConnection *connection,
 		/* metric is not per routes configuration right now
 		 * global metric is also supported (metric="x") */
 		if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL)
-			metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+			metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
 		else {
 			metric_str = ifnet_get_global_data ("metric");
 			if (metric_str) {
 				stripped = g_strdup (metric_str);
 				strip_string (stripped, '"');
-				metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+				metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
 				g_free (stripped);
 			} else
 				metric = 1;
diff --git a/src/settings/plugins/ifnet/net_parser.c b/src/settings/plugins/ifnet/net_parser.c
index 755767e2..8f528d0f 100644
--- a/src/settings/plugins/ifnet/net_parser.c
+++ b/src/settings/plugins/ifnet/net_parser.c
@@ -174,9 +174,9 @@ init_block_by_line (gchar * buf)
 			/* ignored connection */
 			conn = add_new_connection_config ("ignore", pos);
 		} else {
-			int ifindex = nm_platform_link_get_ifindex (pos);
+			int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, pos);
 
-			if (ifindex && nm_platform_link_get_type (ifindex) != NM_LINK_TYPE_WIFI)
+			if (ifindex && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) != NM_LINK_TYPE_WIFI)
 				/* wired connection */
 				conn = add_new_connection_config ("wired", pos);
 			else
diff --git a/src/settings/plugins/ifnet/net_parser.h b/src/settings/plugins/ifnet/net_parser.h
index 005207ad..d10979cc 100644
--- a/src/settings/plugins/ifnet/net_parser.h
+++ b/src/settings/plugins/ifnet/net_parser.h
@@ -25,7 +25,6 @@
 #include <glib.h>
 
 #define CONF_NET_FILE SYSCONFDIR "/conf.d/net"
-#define IFNET_KEY_FILE_GROUP "ifnet"
 
 gboolean ifnet_init (gchar * config_file);
 void ifnet_destroy (void);
diff --git a/src/settings/plugins/ifnet/plugin.c b/src/settings/plugins/ifnet/plugin.c
index 33711639..5eab31ab 100644
--- a/src/settings/plugins/ifnet/plugin.c
+++ b/src/settings/plugins/ifnet/plugin.c
@@ -47,7 +47,6 @@
 #define IFNET_PLUGIN_INFO "(C) 1999-2010 Gentoo Foundation, Inc. To report bugs please use bugs.gentoo.org with [networkmanager] or [qiaomuf] prefix."
 #define IFNET_SYSTEM_HOSTNAME_FILE "/etc/conf.d/hostname"
 #define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE
-#define IFNET_KEY_FILE_KEY_MANAGED "managed"
 
 typedef struct {
 	GHashTable *connections;  /* uuid::connection */
@@ -121,17 +120,9 @@ write_system_hostname (NMSystemConfigInterface * config,
 static gboolean
 is_managed_plugin (void)
 {
-	char *result = NULL;
-
-	result = nm_config_get_value (nm_config_get (),
-	                              IFNET_KEY_FILE_GROUP, IFNET_KEY_FILE_KEY_MANAGED,
-	                              NULL);
-	if (result) {
-		gboolean ret = is_true (result);
-		g_free (result);
-		return ret;
-	}
-	return IFNET_MANAGE_WELL_KNOWN_DEFAULT;
+	return nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+	                                         NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED,
+	                                         IFNET_MANAGE_WELL_KNOWN_DEFAULT);
 }
 
 static void
@@ -246,8 +237,7 @@ reload_connections (NMSystemConfigInterface *config)
 	SCPluginIfnet *self = SC_PLUGIN_IFNET (config);
 	SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self);
 	GList *conn_names = NULL, *n_iter = NULL;
-	gboolean auto_refresh = FALSE;
-	char *str_auto_refresh;
+	gboolean auto_refresh;
 	GError *error = NULL;
 
 	/* save names for removing unused connections */
@@ -264,12 +254,9 @@ reload_connections (NMSystemConfigInterface *config)
 
 	nm_log_info (LOGD_SETTINGS, "Loading connections");
 
-	str_auto_refresh = nm_config_get_value (nm_config_get (),
-	                                        IFNET_KEY_FILE_GROUP, "auto_refresh",
-	                                        NULL);
-	if (str_auto_refresh && is_true (str_auto_refresh))
-		auto_refresh = TRUE;
-	g_free (str_auto_refresh);
+	auto_refresh = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+	                                                 NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH,
+	                                                 FALSE);
 
 	new_connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
 
diff --git a/src/settings/plugins/ifnet/tests/Makefile.in b/src/settings/plugins/ifnet/tests/Makefile.in
index 5b0dcc8d..b94a46fc 100644
--- a/src/settings/plugins/ifnet/tests/Makefile.in
+++ b/src/settings/plugins/ifnet/tests/Makefile.in
@@ -195,6 +195,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -308,6 +309,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ifnet/tests/test_all.c b/src/settings/plugins/ifnet/tests/test_all.c
index 76ed0c7d..55e7e283 100644
--- a/src/settings/plugins/ifnet/tests/test_all.c
+++ b/src/settings/plugins/ifnet/tests/test_all.c
@@ -111,7 +111,7 @@ test_is_static (void)
 		"a dhcp interface is recognized as static");
 	ASSERT (is_static_ip4 ("eth0") == TRUE, "is static",
 		"a static interface is recognized as dhcp");
-	ASSERT (!is_static_ip6 ("eth0") == TRUE, "is static",
+	ASSERT (is_static_ip6 ("eth0") == FALSE, "is static",
 		"a dhcp interface is recognized as static");
 }
 
diff --git a/src/settings/plugins/ifupdown/Makefile.in b/src/settings/plugins/ifupdown/Makefile.in
index 6d0da08c..7cd1ef4e 100644
--- a/src/settings/plugins/ifupdown/Makefile.in
+++ b/src/settings/plugins/ifupdown/Makefile.in
@@ -266,6 +266,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -379,6 +380,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/ifupdown/plugin.c b/src/settings/plugins/ifupdown/plugin.c
index 1b5642af..8652dd8d 100644
--- a/src/settings/plugins/ifupdown/plugin.c
+++ b/src/settings/plugins/ifupdown/plugin.c
@@ -63,8 +63,6 @@
 #define IFUPDOWN_PLUGIN_INFO "(C) 2008 Canonical Ltd.  To report bugs please use the NetworkManager mailing list."
 #define IFUPDOWN_SYSTEM_HOSTNAME_FILE "/etc/hostname"
 
-#define IFUPDOWN_KEY_FILE_GROUP "ifupdown"
-#define IFUPDOWN_KEY_FILE_KEY_MANAGED "managed"
 #define IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT TRUE
 
 /* #define ALWAYS_UNMANAGE TRUE */
@@ -327,8 +325,6 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config)
 	GHashTable *auto_ifaces;
 	if_block *block = NULL;
 	NMInotifyHelper *inotify_helper;
-	char *value;
-	GError *error = NULL;
 	GList *keys, *iter;
 	GHashTableIter con_iter;
 	const char *block_name;
@@ -354,8 +350,6 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config)
 	} else
 		g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
 
-	priv->unmanage_well_known = IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT;
- 
 	inotify_helper = nm_inotify_helper_get ();
 	priv->inotify_event_id = g_signal_connect (inotify_helper,
 	                                           "event",
@@ -456,21 +450,10 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config)
 	g_hash_table_destroy (auto_ifaces);
 
 	/* Check the config file to find out whether to manage interfaces */
-	value = nm_config_get_value (nm_config_get (),
-	                             IFUPDOWN_KEY_FILE_GROUP, IFUPDOWN_KEY_FILE_KEY_MANAGED,
-	                             &error);
-	if (error) {
-		nm_log_info (LOGD_SETTINGS, "loading system config file (%s) caused error: %s",
-		             nm_config_get_path (nm_config_get ()),
-		             error->message);
-	} else {
-		gboolean manage_well_known;
-		error = NULL;
-
-		manage_well_known = !g_strcmp0 (value, "true") || !g_strcmp0 (value, "1");
-		priv->unmanage_well_known = !manage_well_known;
-		g_free (value);
-	}
+	priv->unmanage_well_known = !nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+	                                                               NM_CONFIG_KEYFILE_GROUP_IFUPDOWN,
+	                                                               NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED,
+	                                                               !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT);
 	nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanage_well_known ? "unmanaged" : "managed");
 
 	/* Add well-known interfaces */
@@ -630,7 +613,7 @@ sc_plugin_ifupdown_init (SCPluginIfupdown *plugin)
 
 static void
 GObject__get_property (GObject *object, guint prop_id,
-				   GValue *value, GParamSpec *pspec)
+                       GValue *value, GParamSpec *pspec)
 {
 	NMSystemConfigInterface *self = NM_SYSTEM_CONFIG_INTERFACE (object);
 
diff --git a/src/settings/plugins/ifupdown/tests/Makefile.in b/src/settings/plugins/ifupdown/tests/Makefile.in
index d8c5b39e..a1c029c3 100644
--- a/src/settings/plugins/ifupdown/tests/Makefile.in
+++ b/src/settings/plugins/ifupdown/tests/Makefile.in
@@ -206,13 +206,196 @@ am__tty_colors = { \
     std=''; \
   fi; \
 }
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 am__DIST_COMMON = $(srcdir)/Makefile.in \
-	$(top_srcdir)/build-aux/depcomp
+	$(top_srcdir)/build-aux/depcomp \
+	$(top_srcdir)/build-aux/test-driver
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -326,6 +509,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
@@ -499,7 +683,7 @@ EXTRA_DIST = \
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -660,98 +844,168 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
 	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	elif test -n "$$redo_logs"; then \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
 	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
 	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
 	    else \
-	      skipped="($$skip tests were not run)"; \
+	      color_start= color_end=; \
 	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test-ifupdown.log: test-ifupdown$(EXEEXT)
+	@p='test-ifupdown$(EXEEXT)'; \
+	b='test-ifupdown'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
 
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -808,6 +1062,9 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
@@ -903,7 +1160,7 @@ uninstall-am:
 	installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags tags-am uninstall uninstall-am
+	recheck tags tags-am uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
diff --git a/src/settings/plugins/keyfile/Makefile.in b/src/settings/plugins/keyfile/Makefile.in
index 21c5577c..c409d481 100644
--- a/src/settings/plugins/keyfile/Makefile.in
+++ b/src/settings/plugins/keyfile/Makefile.in
@@ -235,6 +235,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -348,6 +349,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/keyfile/common.h b/src/settings/plugins/keyfile/common.h
index 7bde4bf3..86fe0028 100644
--- a/src/settings/plugins/keyfile/common.h
+++ b/src/settings/plugins/keyfile/common.h
@@ -28,7 +28,5 @@
 
 #define KEYFILE_DIR NMCONFDIR "/system-connections"
 
-#define VPN_SECRETS_GROUP "vpn-secrets"
-
 #endif  /* __COMMON_H__ */
 
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index fcdb3c3c..7f21e71e 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -585,45 +585,19 @@ get_unmanaged_specs (NMSystemConfigInterface *config)
 	GKeyFile *key_file;
 	GSList *specs = NULL;
 	GError *error = NULL;
-	char *str;
 
 	if (!priv->conf_file)
 		return NULL;
 
-	key_file = g_key_file_new ();
-	if (!parse_key_file_allow_none (priv, key_file, &error))
-		goto out;
-
-	str = g_key_file_get_value (key_file, "keyfile", "unmanaged-devices", NULL);
-	if (str) {
-		char **udis;
-		int i;
-
-		udis = g_strsplit_set (str, ";,", -1);
-		g_free (str);
-
-		for (i = 0; udis[i] != NULL; i++) {
-			/* Verify unmanaged specification and add it to the list */
-			if (!strncmp (udis[i], "mac:", 4) && nm_utils_hwaddr_valid (udis[i] + 4, -1)) {
-				specs = g_slist_append (specs, udis[i]);
-			} else if (!strncmp (udis[i], "interface-name:", 15) && nm_utils_iface_valid_name (udis[i] + 15)) {
-				specs = g_slist_append (specs, udis[i]);
-			} else {
-				nm_log_warn (LOGD_SETTINGS, "keyfile: error in file '%s': invalid unmanaged-devices entry: '%s'", priv->conf_file, udis[i]);
-				g_free (udis[i]);
-			}
-		}
+	key_file = nm_config_create_keyfile ();
+	if (parse_key_file_allow_none (priv, key_file, &error))
+		specs = nm_config_get_device_match_spec (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices", NULL);
 
-		g_free (udis); /* Yes, g_free, not g_strfreev because we need the strings in the list */
-	}
-
- out:
 	if (error) {
 		nm_log_warn (LOGD_SETTINGS, "keyfile: error getting unmanaged specs: %s", error->message);
 		g_error_free (error);
 	}
-	if (key_file)
-		g_key_file_free (key_file);
+	g_key_file_free (key_file);
 
 	return specs;
 }
@@ -643,7 +617,7 @@ plugin_get_hostname (SCPluginKeyfile *plugin)
 	if (!parse_key_file_allow_none (priv, key_file, &error))
 		goto out;
 
-	hostname = g_key_file_get_value (key_file, "keyfile", "hostname", NULL);
+	hostname = g_key_file_get_value (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "hostname", NULL);
 
  out:
 	if (error) {
@@ -679,7 +653,7 @@ plugin_set_hostname (SCPluginKeyfile *plugin, const char *hostname)
 	if (!parse_key_file_allow_none (priv, key_file, &error))
 		goto out;
 
-	g_key_file_set_string (key_file, "keyfile", "hostname", hostname);
+	g_key_file_set_string (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "hostname", hostname);
 
 	data = g_key_file_to_data (key_file, &len, &error);
 	if (!data)
@@ -843,7 +817,7 @@ nm_settings_keyfile_plugin_new (void)
 		singleton = SC_PLUGIN_KEYFILE (g_object_new (SC_TYPE_PLUGIN_KEYFILE, NULL));
 		priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (singleton);
 
-		priv->conf_file = nm_config_get_path (nm_config_get ());
+		priv->conf_file = nm_config_data_get_config_main_file (nm_config_get_data (nm_config_get ()));
 
 		/* plugin_set_hostname() has to be called *after* priv->conf_file is set */
 		priv->hostname = plugin_get_hostname (singleton);
diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c
index 1f0953e0..bd08c17d 100644
--- a/src/settings/plugins/keyfile/reader.c
+++ b/src/settings/plugins/keyfile/reader.c
@@ -15,1253 +15,83 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2008 - 2011 Red Hat, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 
-#include <errno.h>
-#include <stdlib.h>
 #include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
 #include <string.h>
 
-#include "nm-core-internal.h"
-#include "nm-dbus-glib-types.h"
-#include "nm-glib-compat.h"
-#include "nm-system-config-interface.h"
-#include "nm-logging.h"
 #include "reader.h"
-#include "common.h"
-#include "utils.h"
-#include "nm-core-internal.h"
-#include "NetworkManagerUtils.h"
-
-/* Some setting properties also contain setting names, such as
- * NMSettingConnection's 'type' property (which specifies the base type of the
- * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave
- * connection, e.g. bond or bridge). This function handles translating those
- * properties' values to the real setting name if they are an alias.
- */
-static void
-setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	char *s;
-	const char *key_setting_name;
-
-	s = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
-	if (s) {
-		key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
-		g_object_set (G_OBJECT (setting),
-		              key, key_setting_name ? key_setting_name : s,
-		              NULL);
-		g_free (s);
-	}
-}
-
-static gboolean
-read_array_of_uint (GKeyFile *file,
-                    NMSetting *setting,
-                    const char *key)
-{
-	GArray *array = NULL;
-	gsize length;
-	int i;
-	gint *tmp;
-
-	tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL);
-	array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length);
-	g_return_val_if_fail (array != NULL, FALSE);
-
-	for (i = 0; i < length; i++)
-		g_array_append_val (array, tmp[i]);
-
-	g_object_set (setting, key, array, NULL);
-	g_array_unref (array);
-
-	return TRUE;
-}
-
-static gboolean
-get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *out)
-{
-	long tmp;
-	char *endptr;
-
-	if (!str || !str[0]) {
-		if (key_name)
-			nm_log_warn (LOGD_SETTINGS, "%s: ignoring missing number %s", __func__, key_name);
-		return FALSE;
-	}
-
-	errno = 0;
-	tmp = strtol (str, &endptr, 10);
-	if (errno || (tmp < 0) || (tmp > max_val) || *endptr != 0) {
-		if (key_name)
-			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid number %s '%s'", __func__, key_name, str);
-		return FALSE;
-	}
-
-	*out = (guint32) tmp;
-	return TRUE;
-}
-
-static gpointer
-build_address (int family, const char *address_str, guint32 plen)
-{
-	NMIPAddress *addr;
-	GError *error = NULL;
-
-	g_return_val_if_fail (address_str, NULL);
-
-	addr = nm_ip_address_new (family, address_str, plen, &error);
-	if (!addr) {
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s address: %s", __func__,
-		             family == AF_INET ? "IPv4" : "IPv6",
-		             error->message);
-		g_error_free (error);
-	}
-
-	return addr;
-}
-
-static gpointer
-build_route (int family,
-             const char *dest_str, guint32 plen,
-             const char *gateway_str, const char *metric_str,
-             const char *key_name)
-{
-	NMIPRoute *route;
-	guint32 metric = 0;
-	GError *error = NULL;
-
-	g_return_val_if_fail (plen, NULL);
-	g_return_val_if_fail (dest_str, NULL);
-
-	/* Next hop */
-	if (gateway_str && gateway_str[0]) {
-		if (!nm_utils_ipaddr_valid (family, gateway_str)) {
-			/* Try workaround for routes written by broken keyfile writer.
-			 * Due to bug bgo#719851, an older version of writer would have
-			 * written "a:b:c:d::/plen,metric" if the gateway was ::, instead
-			 * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric"
-			 * Try workaround by interpreting gateway_str as metric to accept such
-			 * invalid routes. This broken syntax should not be not officially
-			 * supported.
-			 **/
-			if (   family == AF_INET6
-			    && !metric_str
-			    && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric))
-				gateway_str = NULL;
-			else {
-				nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid gateway '%s'", __func__, gateway_str);
-				return NULL;
-			}
-		}
-	} else
-		gateway_str = NULL;
-
-	/* parse metric, default to 0 */
-	if (metric_str) {
-		if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
-			return NULL;
-	}
-
-	route = nm_ip_route_new (family, dest_str, plen, gateway_str,
-	                         metric ? (gint64) metric : -1,
-	                         &error);
-	if (!route) {
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s route: %s", __func__,
-		             family == AF_INET ? "IPv4" : "IPv6",
-		             error->message);
-		g_error_free (error);
-	}
-
-	return route;
-}
-
-/* On success, returns pointer to the zero-terminated field (original @current).
- * The @current * pointer target is set to point to the rest of the input
- * or %NULL if there is no more input. Sets error to %NULL for convenience.
- *
- * On failure, returns %NULL (unspecified). The @current pointer target is
- * resets to its original value to allow skipping fields. The @error target
- * is set to the character that breaks the parsing or %NULL if @current was %NULL.
- *
- * When @current target is %NULL, gracefully fail returning %NULL while
- * leaving the @current target %NULL end setting @error to %NULL;
- */
-static char *
-read_field (char **current, char **error, const char *characters, const char *delimiters)
-{
-	char *start;
-
-	g_return_val_if_fail (current, NULL);
-	g_return_val_if_fail (error, NULL);
-	g_return_val_if_fail (characters, NULL);
-	g_return_val_if_fail (delimiters, NULL);
-
-	if (!*current) {
-		/* graceful failure, leave '*current' NULL */
-		*error = NULL;
-		return NULL;
-	}
-
-	/* fail on empty input */
-	if (!**current)
-		return NULL;
-
-	/* remember beginning of input */
-	start = *current;
-
-	while (**current && strchr (characters, **current))
-		(*current)++;
-	if (**current)
-		if (strchr (delimiters, **current)) {
-			/* success, more data available */
-			*error = NULL;
-			*(*current)++ = '\0';
-			return start;
-		} else {
-			/* error, bad character */
-			*error = *current;
-			*current = start;
-			return NULL;
-		}
-	else {
-		/* success, end of input */
-		*error = NULL;
-		*current = NULL;
-		return start;
-	}
-}
-
-#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%"
-#define DIGITS "0123456789"
-#define DELIMITERS "/;,"
-
-
-/* The following IPv4 and IPv6 address formats are supported:
- *
- * address (DEPRECATED)
- * address/plen
- * address/gateway (DEPRECATED)
- * address/plen,gateway
- *
- * The following IPv4 and IPv6 route formats are supported:
- *
- * address/plen (NETWORK dev DEVICE)
- * address/plen,gateway (NETWORK via GATEWAY dev DEVICE)
- * address/plen,,metric (NETWORK dev DEVICE metric METRIC)
- * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
- *
- * For backward, forward and sideward compatibility, slash (/),
- * semicolon (;) and comma (,) are interchangable. The choice of
- * separator in the above examples is therefore not significant.
- *
- * Leaving out the prefix length is discouraged and DEPRECATED. The
- * default value of IPv6 prefix length was 64 and has not been
- * changed. The default for IPv4 is now 24, which is the closest
- * IPv4 equivalent. These defaults may just as well be changed to
- * match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
- */
-static gpointer
-read_one_ip_address_or_route (GKeyFile *file,
-                              const char *setting_name,
-                              const char *key_name,
-                              gboolean ipv6,
-                              gboolean route,
-                              char **out_gateway)
-{
-	guint32 plen = G_MAXUINT32;
-	gpointer result;
-	char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;
-
-	current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL);
-	if (!value)
-		return NULL;
-
-	/* get address field */
-	address_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
-	if (error) {
-		nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
-		             *error, setting_name, key_name, error - current, current);
-		goto error;
-	}
-	/* get prefix length field (skippable) */
-	plen_str = read_field (&current, &error, DIGITS, DELIMITERS);
-	/* get gateway field */
-	gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
-	if (error) {
-		nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
-		             *error, setting_name, key_name,
-		             plen_str ? "gateway" : "gateway or prefix length",
-		             error - current, current);
-		goto error;
-	}
-	/* for routes, get metric */
-	if (route) {
-		metric_str = read_field (&current, &error, DIGITS, DELIMITERS);
-		if (error) {
-			nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
-			             *error, setting_name, key_name, error - current, current);
-			goto error;
-		}
-	} else
-		metric_str = NULL;
-	if (current) {
-		/* there is still some data */
-		if (*current) {
-			/* another field follows */
-			nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: Garbage at the and of the line: %s",
-			             setting_name, key_name, current);
-			goto error;
-		} else {
-			/* semicolon at the end of input */
-			nm_log_info (LOGD_SETTINGS, "keyfile: %s.%s: Deprecated semicolon at the end of value.",
-			             setting_name, key_name);
-		}
-	}
-
-#define DEFAULT_PREFIX(for_route, for_ipv6) ( (for_route) ? ( (for_ipv6) ? 128 : 24 ) : ( (for_ipv6) ? 64 : 24 ) )
-
-	/* parse plen, fallback to defaults */
-	if (plen_str) {
-		if (!get_one_int (plen_str, ipv6 ? 128 : 32, key_name, &plen)
-		    || (route && plen == 0)) {
-			plen = DEFAULT_PREFIX (route, ipv6);
-			nm_log_warn (LOGD_SETTINGS, "keyfile: invalid prefix length '%s' in '%s.%s', defaulting to %d",
-			             plen_str, setting_name, key_name, plen);
-		}
-	} else {
-		plen = DEFAULT_PREFIX (route, ipv6);
-		nm_log_warn (LOGD_SETTINGS, "keyfile: Missing prefix length in '%s.%s', defaulting to %d",
-		             setting_name, key_name, plen);
-	}
-
-	/* build the appropriate data structure for NetworkManager settings */
-	if (route) {
-		result = build_route (ipv6 ? AF_INET6 : AF_INET,
-		                      address_str, plen, gateway_str, metric_str,
-		                      key_name);
-	} else {
-		result = build_address (ipv6 ? AF_INET6 : AF_INET,
-		                        address_str, plen);
-		if (out_gateway && gateway_str)
-			*out_gateway = g_strdup (gateway_str);
-	}
 
-	g_free (value);
-	return result;
-error:
-	g_free (value);
-	return NULL;
-}
+#include "nm-logging.h"
+#include "nm-keyfile-internal.h"
+#include "NetworkManagerUtils.h"
 
-static void
-ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+static const char *
+_fmt_warn (const char *group, NMSetting *setting, const char *property_name, const char *message, char **out_message)
 {
-	const char *setting_name = nm_setting_get_name (setting);
-	gboolean ipv6 = !strcmp (setting_name, "ipv6");
-	gboolean routes = !strcmp (key, "routes");
-	static const char *key_names_routes[] = { "route", "routes", NULL };
-	static const char *key_names_addresses[] = { "address", "addresses", NULL };
-	const char **key_names = routes ? key_names_routes : key_names_addresses;
-	char *gateway = NULL;
-	GPtrArray *list;
-	GDestroyNotify free_func;
-	int i;
-
-	if (routes)
-		free_func = (GDestroyNotify) nm_ip_route_unref;
-	else
-		free_func = (GDestroyNotify) nm_ip_address_unref;
-	list = g_ptr_array_new_with_free_func (free_func);
+	const char *setting_name = setting ? nm_setting_get_name (setting) : NULL;
 
-	for (i = -1; i < 1000; i++) {
-		const char **key_basename;
-		
-		for (key_basename = key_names; *key_basename; key_basename++) {
-			char *key_name;
-			gpointer item;
+	if (group) {
+		char *res;
 
-			/* -1 means no suffix */
-			if (i >= 0)
-				key_name = g_strdup_printf ("%s%d", *key_basename, i);
+		if (setting_name) {
+			if (property_name && !strcmp (group, setting_name))
+				res = g_strdup_printf ("%s.%s: %s", group, property_name, message);
+			else if (property_name)
+				res = g_strdup_printf ("%s/%s.%s: %s", group, setting_name, property_name, message);
+			else if (!strcmp (group, setting_name))
+				res = g_strdup_printf ("%s: %s", group, message);
 			else
-				key_name = g_strdup (*key_basename);
-
-			item = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, routes,
-			                                     gateway ? NULL : &gateway);
-			if (item)
-				g_ptr_array_add (list, item);
-
-			g_free (key_name);
-		}
-	}
-
-	if (list->len >= 1)
-		g_object_set (setting, key, list, NULL);
-
-	if (gateway) {
-		g_object_set (setting, "gateway", gateway, NULL);
-		g_free (gateway);
-	}
-
-	g_ptr_array_unref (list);
-}
-
-static void
-ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GPtrArray *array;
-	gsize length;
-	char **list, **iter;
-	int ret;
-
-	list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
-	if (!list || !g_strv_length (list))
-		return;
-
-	array = g_ptr_array_sized_new (length + 1);
-	for (iter = list; *iter; iter++) {
-		guint32 addr;
-
-		ret = inet_pton (AF_INET, *iter, &addr);
-		if (ret <= 0) {
-			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server address '%s'", __func__, *iter);
-			continue;
-		}
-
-		g_ptr_array_add (array, *iter);
-	}
-	g_ptr_array_add (array, NULL);
-
-	g_object_set (setting, key, array->pdata, NULL);
-	g_ptr_array_unref (array);
-	g_strfreev (list);
-}
-
-static void
-ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GPtrArray *array = NULL;
-	gsize length;
-	char **list, **iter;
-	int ret;
-
-	list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
-	if (!list || !g_strv_length (list))
-		return;
-
-	array = g_ptr_array_sized_new (length + 1);
-
-	for (iter = list; *iter; iter++) {
-		struct in6_addr addr;
-
-		ret = inet_pton (AF_INET6, *iter, &addr);
-		if (ret <= 0) {
-			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter);
-			continue;
-		}
-
-		g_ptr_array_add (array, *iter);
-	}
-	g_ptr_array_add (array, NULL);
-
-	g_object_set (setting, key, array->pdata, NULL);
-	g_ptr_array_unref (array);
-	g_strfreev (list);
-}
-
-static void
-mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path, gsize enforce_length)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	char *tmp_string = NULL, *p, *mac_str;
-	gint *tmp_list;
-	GByteArray *array = NULL;
-	gsize length;
-
-	p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
-	if (tmp_string && tmp_string[0]) {
-		/* Look for enough ':' characters to signify a MAC address */
-		guint i = 0;
-
-		while (*p) {
-			if (*p == ':')
-				i++;
-			p++;
-		}
-
-		if (enforce_length == 0 || enforce_length == i+1) {
-			/* If we found enough it's probably a string-format MAC address */
-			array = g_byte_array_sized_new (i+1);
-			g_byte_array_set_size (array, i+1);
-			if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) {
-				g_byte_array_unref (array);
-				array = NULL;
-			}
-		}
-	}
-	g_free (tmp_string);
-
-	if (array == NULL) {
-		/* Old format; list of ints */
-		tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
-		if (length > 0 && (enforce_length == 0 || enforce_length == length)) {
-			gsize i;
-
-			array = g_byte_array_sized_new (length);
-			for (i = 0; i < length; i++) {
-				int val = tmp_list[i];
-				const guint8 v = (guint8) (val & 0xFF);
-
-				if (val < 0 || val > 255) {
-					nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
-					             " between 0 and 255 inclusive)", __func__, setting_name,
-					             key, val);
-					g_byte_array_free (array, TRUE);
-					array = NULL;
-					break;
-				}
-				g_byte_array_append (array, &v, 1);
-			}
-		}
-		g_free (tmp_list);
-	}
-
-	if (!array) {
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid MAC address for %s / %s",
-		             __func__, setting_name, key);
-		return;
-	}
-
-	mac_str = nm_utils_hwaddr_ntoa (array->data, array->len);
-	g_object_set (setting, key, mac_str, NULL);
-	g_free (mac_str);
-	g_byte_array_free (array, TRUE);
-}
-
-static void
-mac_address_parser_ETHER (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	mac_address_parser (setting, key, keyfile, keyfile_path, ETH_ALEN);
-}
-
-static void
-mac_address_parser_INFINIBAND (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	mac_address_parser (setting, key, keyfile, keyfile_path, INFINIBAND_ALEN);
-}
-
-static void
-read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
-{
-	char **keys, **iter;
-	char *value;
-	const char *setting_name = nm_setting_get_name (setting);
-
-	keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL);
-	if (!keys || !*keys)
-		return;
-
-	for (iter = keys; *iter; iter++) {
-		value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL);
-		if (!value)
-			continue;
-
-		if (NM_IS_SETTING_VPN (setting)) {
-			/* Add any item that's not a class property to the data hash */
-			if (!g_object_class_find_property (G_OBJECT_GET_CLASS (setting), *iter))
-				nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value);
-		}
-		if (NM_IS_SETTING_BOND (setting)) {
-			if (strcmp (*iter, "interface-name"))
-				nm_setting_bond_add_option (NM_SETTING_BOND (setting), *iter, value);
-		}
-		g_free (value);
-	}
-	g_strfreev (keys);
-}
-
-static void
-unescape_semicolons (char *str)
-{
-	int i;
-	gsize len = strlen (str);
-
-	for (i = 0; i < len; i++) {
-		if (str[i] == '\\' && str[i+1] == ';') {
-			memmove(str + i, str + i + 1, len - (i + 1));
-			len--;
-		}
-		str[len] = '\0';
-	}
-}
-
-static GBytes *
-get_bytes (GKeyFile *keyfile,
-           const char *setting_name,
-           const char *key,
-           gboolean zero_terminate,
-           gboolean unescape_semicolon)
-{
-	GByteArray *array = NULL;
-	char *tmp_string;
-	gint *tmp_list;
-	gsize length;
-	int i;
-
-	/* New format: just a string
-	 * Old format: integer list; e.g. 11;25;38;
-	 */
-	tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
-	if (tmp_string) {
-		GRegex *regex;
-		GMatchInfo *match_info;
-		const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*;([[:space:]]*[[:digit:]]{1,3}[[:space:]]*;)*([[:space:]]*)?$";
-
-		regex = g_regex_new (pattern, 0, 0, NULL);
-		g_regex_match (regex, tmp_string, 0, &match_info);
-		if (!g_match_info_matches (match_info)) {
-			/* Handle as a simple string (ie, new format) */
-			if (unescape_semicolon)
-				unescape_semicolons (tmp_string);
-			length = strlen (tmp_string);
-			if (zero_terminate)
-				length++;
-			array = g_byte_array_sized_new (length);
-			g_byte_array_append (array, (guint8 *) tmp_string, length);
-		}
-		g_match_info_free (match_info);
-		g_regex_unref (regex);
-		g_free (tmp_string);
-	}
-
-	if (!array) {
-		/* Old format; list of ints */
-		tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
-		array = g_byte_array_sized_new (length);
-		for (i = 0; i < length; i++) {
-			int val = tmp_list[i];
-			unsigned char v = (unsigned char) (val & 0xFF);
-
-			if (val < 0 || val > 255) {
-				nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
-				             " between 0 and 255 inclusive)", __func__, setting_name,
-				             key, val);
-			} else
-				g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
-		}
-		g_free (tmp_list);
-	}
-
-	if (array->len == 0) {
-		g_byte_array_free (array, TRUE);
-		return NULL;
+				res = g_strdup_printf ("%s/%s: %s", group, setting_name, message);
+		} else
+			res = g_strdup_printf ("%s: %s", group, message);
+		*out_message = res;
+		return res;
 	} else
-		return g_byte_array_free_to_bytes (array);
-}
-
-static void
-ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GBytes *bytes;
-
-	bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE);
-	if (bytes) {
-		g_object_set (setting, key, bytes, NULL);
-		g_bytes_unref (bytes);
-	} else {
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid SSID for %s / %s",
-		             __func__, setting_name, key);
-	}
-}
-
-static void
-password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GBytes *bytes;
-
-	bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE);
-	if (bytes) {
-		g_object_set (setting, key, bytes, NULL);
-		g_bytes_unref (bytes);
-	} else {
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid raw password for %s / %s",
-		             __func__, setting_name, key);
-	}
-}
-
-static char *
-get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_path_len)
-{
-	const char *base;
-	char *p = NULL, *path, *dirname, *tmp;
-
-	g_return_val_if_fail (keyfile_path != NULL, NULL);
-	g_return_val_if_fail (cert_path != NULL, NULL);
-
-	base = path = g_malloc0 (cert_path_len + 1);
-	memcpy (path, cert_path, cert_path_len);
-
-	if (path[0] == '/')
-		return path;
-
-	p = strrchr (path, '/');
-	if (p)
-		base = p + 1;
-
-	dirname = g_path_get_dirname (keyfile_path);
-	tmp = g_build_path ("/", dirname, base, NULL);
-	g_free (dirname);
-	g_free (path);
-	return tmp;
-}
-
-#define SCHEME_PATH "file://"
-
-static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
-
-static gboolean
-has_cert_ext (const char *path)
-{
-	int i;
-
-	for (i = 0; i < G_N_ELEMENTS (certext); i++) {
-		if (g_str_has_suffix (path, certext[i]))
-			return TRUE;
-	}
-	return FALSE;
+		return message;
 }
 
 static gboolean
-handle_as_scheme (GBytes *bytes, NMSetting *setting, const char *key)
-{
-	const guint8 *data;
-	gsize data_len;
-
-	data = g_bytes_get_data (bytes, &data_len);
+_handler_read (GKeyFile *keyfile,
+               NMConnection *connection,
+               NMKeyfileReadType type,
+               void *type_data,
+               void *user_data,
+               GError **error)
+{
+	if (type == NM_KEYFILE_READ_TYPE_WARN) {
+		NMKeyfileReadTypeDataWarn *warn_data = type_data;
+		NMLogLevel level;
+		char *message_free = NULL;
+
+		if (warn_data->severity > NM_KEYFILE_WARN_SEVERITY_WARN)
+			level = LOGL_ERR;
+		else if (warn_data->severity >= NM_KEYFILE_WARN_SEVERITY_WARN)
+			level = LOGL_WARN;
+		else if (warn_data->severity == NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE)
+			level = LOGL_WARN;
+		else
+			level = LOGL_INFO;
 
-	/* It's the PATH scheme, can just set plain data */
-	if (   (data_len > strlen (SCHEME_PATH))
-	    && g_str_has_prefix ((const char *) data, SCHEME_PATH)
-	    && (data[data_len - 1] == '\0')) {
-		g_object_set (setting, key, bytes, NULL);
+		nm_log (level, LOGD_SETTINGS, "keyfile: %s",
+		        _fmt_warn (warn_data->group, warn_data->setting,
+		                   warn_data->property_name, warn_data->message,
+		                   &message_free));
+		g_free (message_free);
 		return TRUE;
 	}
 	return FALSE;
 }
 
-static gboolean
-handle_as_path (GBytes *bytes,
-                NMSetting *setting,
-                const char *key,
-                const char *keyfile_path)
-{
-	const guint8 *data;
-	gsize data_len;
-	gsize validate_len;
-	char *path;
-	gboolean exists, success = FALSE;
-
-	data = g_bytes_get_data (bytes, &data_len);
-	if (data_len > 500 || data_len < 1)
-		return FALSE;
-
-	/* If there's a trailing NULL tell g_utf8_validate() to to until the NULL */
-	if (data[data_len - 1] == '\0')
-		validate_len = -1;
-	else
-		validate_len = data_len;
-
-	if (g_utf8_validate ((const char *) data, validate_len, NULL) == FALSE)
-		return FALSE;
-
-	/* Might be a bare path without the file:// prefix; in that case
-	 * if it's an absolute path, use that, otherwise treat it as a
-	 * relative path to the current directory.
-	 */
-
-	path = get_cert_path (keyfile_path, data, data_len);
-	exists = g_file_test (path, G_FILE_TEST_EXISTS);
-	if (   exists
-	    || memchr (data, '/', data_len)
-	    || has_cert_ext (path)) {
-		GByteArray *tmp;
-		GBytes *val;
-
-		/* Construct the proper value as required for the PATH scheme */
-		tmp = g_byte_array_sized_new (strlen (SCHEME_PATH) + strlen (path) + 1);
-		g_byte_array_append (tmp, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
-		g_byte_array_append (tmp, (const guint8 *) path, strlen (path));
-		g_byte_array_append (tmp, (const guint8 *) "\0", 1);
-		val = g_byte_array_free_to_bytes (tmp);
-		g_object_set (setting, key, val, NULL);
-		g_bytes_unref (val);
-		success = TRUE;
-
-		/* Warn if the certificate didn't exist */
-		if (exists == FALSE)
-			nm_log_warn (LOGD_SETTINGS, "certificate or key %s does not exist", path);
-	}
-	g_free (path);
-
-	return success;
-}
-
-static void
-cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GBytes *bytes;
-	gboolean success = FALSE;
-
-	bytes = get_bytes (keyfile, setting_name, key, TRUE, FALSE);
-	if (bytes) {
-		/* Try as a path + scheme (ie, starts with "file://") */
-		success = handle_as_scheme (bytes, setting, key);
-
-		/* If not, it might be a plain path */
-		if (success == FALSE)
-			success = handle_as_path (bytes, setting, key, keyfile_path);
-
-		/* If neither of those two, assume blob with certificate data */
-		if (success == FALSE)
-			g_object_set (setting, key, bytes, NULL);
-	} else {
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid key/cert value for %s / %s",
-		             __func__, setting_name, key);
-	}
-
-	if (bytes)
-		g_bytes_unref (bytes);
-}
-
-static void
-parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	NMSettingSerialParity parity;
-	int int_val;
-	char *str_val;
-
-	/* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'.
-	 * We now accept either that or the (case-insensitive) character itself (but
-	 * still always write it the old way, for backward compatibility).
-	 */
-	int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
-	if (!int_val) {
-		str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
-		if (str_val) {
-			if (str_val[0] && !str_val[1])
-				int_val = str_val[0];
-			else {
-				/* This will hit the warning below */
-				int_val = 'X';
-			}
-		}
-		g_free (str_val);
-	}
-
-	if (!int_val)
-		return;
-
-	switch (int_val) {
-	case 'E':
-	case 'e':
-		parity = NM_SETTING_SERIAL_PARITY_EVEN;
-		break;
-	case 'O':
-	case 'o':
-		parity = NM_SETTING_SERIAL_PARITY_ODD;
-		break;
-	case 'N':
-	case 'n':
-		parity = NM_SETTING_SERIAL_PARITY_NONE;
-		break;
-	default:
-		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid value for %s / %s",
-		             __func__, setting_name, key);
-		return;
-	}
-
-	g_object_set (setting, key, parity, NULL);
-}
-
-typedef struct {
-	const char *setting_name;
-	const char *key;
-	gboolean check_for_key;
-	void (*parser) (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path);
-} KeyParser;
-
-/* A table of keys that require further parsing/conversion because they are
- * stored in a format that can't be automatically read using the key's type.
- * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
- * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
- * in struct in6_addr internally, but as string in keyfiles.
- */
-static KeyParser key_parsers[] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME,
-	  NM_SETTING_CONNECTION_TYPE,
-	  TRUE,
-	  setting_alias_parser },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,
-	  NM_SETTING_BRIDGE_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ADDRESSES,
-	  FALSE,
-	  ip_address_or_route_parser },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ADDRESSES,
-	  FALSE,
-	  ip_address_or_route_parser },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ROUTES,
-	  FALSE,
-	  ip_address_or_route_parser },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ROUTES,
-	  FALSE,
-	  ip_address_or_route_parser },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_DNS,
-	  FALSE,
-	  ip4_dns_parser },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_DNS,
-	  FALSE,
-	  ip6_dns_parser },
-	{ NM_SETTING_WIRED_SETTING_NAME,
-	  NM_SETTING_WIRED_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_WIRED_SETTING_NAME,
-	  NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,
-	  NM_SETTING_WIRELESS_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,
-	  NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,
-	  NM_SETTING_WIRELESS_BSSID,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_BLUETOOTH_SETTING_NAME,
-	  NM_SETTING_BLUETOOTH_BDADDR,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME,
-	  NM_SETTING_INFINIBAND_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_INFINIBAND },
-	{ NM_SETTING_WIMAX_SETTING_NAME,
-	  NM_SETTING_WIMAX_MAC_ADDRESS,
-	  TRUE,
-	  mac_address_parser_ETHER },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,
-	  NM_SETTING_WIRELESS_SSID,
-	  TRUE,
-	  ssid_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PASSWORD_RAW,
-	  TRUE,
-	  password_raw_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_CA_CERT,
-	  TRUE,
-	  cert_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_CLIENT_CERT,
-	  TRUE,
-	  cert_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PRIVATE_KEY,
-	  TRUE,
-	  cert_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PHASE2_CA_CERT,
-	  TRUE,
-	  cert_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	  TRUE,
-	  cert_parser },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	  TRUE,
-	  cert_parser },
-	{ NM_SETTING_SERIAL_SETTING_NAME,
-	  NM_SETTING_SERIAL_PARITY,
-	  TRUE,
-	  parity_parser },
-	{ NULL, NULL, FALSE }
-};
-
-typedef struct {
-	GKeyFile *keyfile;
-	const char *keyfile_path;
-} ReadInfo;
-
-static void
-read_one_setting_value (NMSetting *setting,
-                        const char *key,
-                        const GValue *value,
-                        GParamFlags flags,
-                        gpointer user_data)
-{
-	ReadInfo *info = user_data;
-	const char *setting_name;
-	int errsv;
-	GType type;
-	GError *err = NULL;
-	gboolean check_for_key = TRUE;
-	KeyParser *parser = &key_parsers[0];
-
-	/* Property is not writable */
-	if (!(flags & G_PARAM_WRITABLE))
-		return;
-
-	/* Setting name gets picked up from the keyfile's section name instead */
-	if (!strcmp (key, NM_SETTING_NAME))
-		return;
-
-	/* Don't read the NMSettingConnection object's 'read-only' property */
-	if (   NM_IS_SETTING_CONNECTION (setting)
-	    && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
-		return;
-
-	setting_name = nm_setting_get_name (setting);
-
-	/* Look through the list of handlers for non-standard format key values */
-	while (parser->setting_name) {
-		if (!strcmp (parser->setting_name, setting_name) && !strcmp (parser->key, key)) {
-			check_for_key = parser->check_for_key;
-			break;
-		}
-		parser++;
-	}
-
-	/* VPN properties don't have the exact key name */
-	if (NM_IS_SETTING_VPN (setting))
-		check_for_key = FALSE;
-
-	/* Bonding 'options' don't have the exact key name. The options are right under [bond] group. */
-	if (NM_IS_SETTING_BOND (setting))
-		check_for_key = FALSE;
-
-	/* Check for the exact key in the GKeyFile if required.  Most setting
-	 * properties map 1:1 to a key in the GKeyFile, but for those properties
-	 * like IP addresses and routes where more than one value is actually
-	 * encoded by the setting property, this won't be true.
-	 */
-	if (check_for_key && !nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, &err)) {
-		/* Key doesn't exist or an error ocurred, thus nothing to do. */
-		if (err) {
-			nm_log_warn (LOGD_SETTINGS, "Error loading setting '%s' value: %s", setting_name, err->message);
-			g_error_free (err);
-		}
-		return;
-	}
-
-	/* If there's a custom parser for this key, handle that before the generic
-	 * parsers below.
-	 */
-	if (parser->setting_name) {
-		(*parser->parser) (setting, key, info->keyfile, info->keyfile_path);
-		return;
-	}
-
-	type = G_VALUE_TYPE (value);
-
-	if (type == G_TYPE_STRING) {
-		char *str_val;
-
-		str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
-		g_object_set (setting, key, str_val, NULL);
-		g_free (str_val);
-	} else if (type == G_TYPE_UINT) {
-		int int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
-		if (int_val < 0)
-			nm_log_warn (LOGD_SETTINGS, "Casting negative value (%i) to uint", int_val);
-		g_object_set (setting, key, int_val, NULL);
-	} else if (type == G_TYPE_INT) {
-		int int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
-		g_object_set (setting, key, int_val, NULL);
-	} else if (type == G_TYPE_BOOLEAN) {
-		gboolean bool_val;
-
-		bool_val = nm_keyfile_plugin_kf_get_boolean (info->keyfile, setting_name, key, NULL);
-		g_object_set (setting, key, bool_val, NULL);
-	} else if (type == G_TYPE_CHAR) {
-		int int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
-		if (int_val < G_MININT8 || int_val > G_MAXINT8)
-			nm_log_warn (LOGD_SETTINGS, "Casting value (%i) to char", int_val);
-
-		g_object_set (setting, key, int_val, NULL);
-	} else if (type == G_TYPE_UINT64) {
-		char *tmp_str;
-		guint64 uint_val;
-
-		tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL);
-		uint_val = g_ascii_strtoull (tmp_str, NULL, 10);
-		g_free (tmp_str);
-		g_object_set (setting, key, uint_val, NULL);
-	} else if (type == G_TYPE_INT64) {
-		char *tmp_str;
-		gint64 int_val;
-
-		tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL);
-		int_val = nm_utils_ascii_str_to_int64 (tmp_str, 10, G_MININT64, G_MAXINT64, 0);
-		errsv = errno;
-		if (errsv)
-			nm_log_warn (LOGD_SETTINGS, "Invalid int64 value (%s)", tmp_str);
-		else
-			g_object_set (setting, key, int_val, NULL);
-		g_free (tmp_str);
- 	} else if (type == G_TYPE_BYTES) {
-		gint *tmp;
-		GByteArray *array;
-		GBytes *bytes;
-		gsize length;
-		int i;
-
-		tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
-
-		array = g_byte_array_sized_new (length);
-		for (i = 0; i < length; i++) {
-			int val = tmp[i];
-			unsigned char v = (unsigned char) (val & 0xFF);
-
-			if (val < 0 || val > 255) {
-				nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
-				             " between 0 and 255 inclusive)", __func__, setting_name,
-				             key, val);
-			} else
-				g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
-		}
-
-		bytes = g_byte_array_free_to_bytes (array);
-		g_object_set (setting, key, bytes, NULL);
-		g_bytes_unref (bytes);
-		g_free (tmp);
-	} else if (type == G_TYPE_STRV) {
-		gchar **sa;
-		gsize length;
-
-		sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
-		g_object_set (setting, key, sa, NULL);
-		g_strfreev (sa);
-	} else if (type == G_TYPE_HASH_TABLE) {
-		read_hash_of_string (info->keyfile, setting, key);
-	} else if (type == G_TYPE_ARRAY) {
-		if (!read_array_of_uint (info->keyfile, setting, key)) {
-			nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'",
-			             setting_name, key, G_VALUE_TYPE_NAME (value));
-		}
-	} else if (G_VALUE_HOLDS_FLAGS (value)) {
-		guint64 uint_val;
-
-		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
-		uint_val = nm_keyfile_plugin_kf_get_uint64 (info->keyfile, setting_name, key, &err);
-		if (!err) {
-			if (uint_val <= G_MAXUINT)
-				g_object_set (setting, key, (guint) uint_val, NULL);
-			else {
-				nm_log_warn (LOGD_SETTINGS, "Too large FLAGS property (read): '%s/%s' : '%s'",
-				             setting_name, key, G_VALUE_TYPE_NAME (value));
-			}
-		}
-		g_clear_error (&err);
-	} else if (G_VALUE_HOLDS_ENUM (value)) {
-		gint int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, &err);
-		if (!err)
-			g_object_set (setting, key, (gint) int_val, NULL);
-		g_clear_error (&err);
-	} else {
-		nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'",
-		             setting_name, key, G_VALUE_TYPE_NAME (value));
-	}
-}
-
-static NMSetting *
-read_setting (GKeyFile *file, const char *keyfile_path, const char *group)
-{
-	NMSetting *setting = NULL;
-	ReadInfo info = { file, keyfile_path };
-	const char *alias;
-	GType type;
-
-	alias = nm_keyfile_plugin_get_setting_name_for_alias (group);
-	if (alias)
-		group = alias;
-
-	type = nm_setting_lookup_type (group);
-	if (type) {
-		setting = g_object_new (type, NULL);
-		nm_setting_enumerate_values (setting, read_one_setting_value, &info);
-	} else
-		nm_log_warn (LOGD_SETTINGS, "Invalid setting name '%s'", group);
-
-	return setting;
-}
-
-static void
-read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn)
-{
-	char **keys, **iter;
-
-	keys = nm_keyfile_plugin_kf_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL);
-	for (iter = keys; *iter; iter++) {
-		char *secret;
-
-		secret = nm_keyfile_plugin_kf_get_string (file, VPN_SECRETS_GROUP, *iter, NULL);
-		if (secret) {
-			nm_setting_vpn_add_secret (s_vpn, *iter, secret);
-			g_free (secret);
-		}
-	}
-	g_strfreev (keys);
-}
-
 NMConnection *
 nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
 {
 	GKeyFile *key_file;
 	struct stat statbuf;
-	gboolean bad_permissions;
 	NMConnection *connection = NULL;
-	NMSettingConnection *s_con;
-	NMSetting *setting;
-	gchar **groups;
-	gsize length;
-	int i;
-	gboolean vpn_secrets = FALSE;
 	GError *verify_error = NULL;
 
 	if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
@@ -1270,90 +100,34 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
 		return NULL;
 	}
 
-	bad_permissions = statbuf.st_mode & 0077;
-
-	if (bad_permissions) {
+	if (statbuf.st_mode & 0077) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "File permissions (%o) were insecure",
 		             statbuf.st_mode);
 		return NULL;
 	}
 
-	key_file = g_key_file_new ();
-	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
-		goto out;
-
-	connection = nm_simple_connection_new ();
-
-	groups = g_key_file_get_groups (key_file, &length);
-	for (i = 0; i < length; i++) {
-		/* Only read out secrets when needed */
-		if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
-			vpn_secrets = TRUE;
-			continue;
-		}
-
-		setting = read_setting (key_file, filename, groups[i]);
-		if (setting)
-			nm_connection_add_setting (connection, setting);
-	}
-
-	s_con = nm_connection_get_setting_connection (connection);
-	if (!s_con) {
-		s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-		nm_connection_add_setting (connection, NM_SETTING (s_con));
-	}
-
-	/* Make sure that we have 'id' even if not explictly specified in the keyfile */
-	if (!nm_setting_connection_get_id (s_con)) {
-		char *base_name;
-
-		base_name = g_path_get_basename (filename);
-		g_object_set (s_con, NM_SETTING_CONNECTION_ID, base_name, NULL);
-		g_free (base_name);
-	}
-
-	/* Make sure that we have 'uuid' even if not explictly specified in the keyfile */
-	if (!nm_setting_connection_get_uuid (s_con)) {
-		char *hashed_uuid;
-
-		hashed_uuid = nm_utils_uuid_generate_from_strings ("keyfile", filename, NULL);
-		g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL);
-		g_free (hashed_uuid);
-	}
-
-	/* Make sure that we have 'interface-name' even if it was specified in the
-	 * "wrong" (ie, deprecated) group.
-	 */
-	if (   !nm_setting_connection_get_interface_name (s_con)
-	    && nm_setting_connection_get_connection_type (s_con)) {
-		char *interface_name;
-
-		interface_name = g_key_file_get_string (key_file,
-		                                        nm_setting_connection_get_connection_type (s_con),
-		                                        "interface-name",
-		                                        NULL);
-		if (interface_name) {
-			g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL);
-			g_free (interface_name);
+	if (!NM_FLAGS_HAS (nm_utils_get_testing (), NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK)) {
+		if (statbuf.st_uid != 0) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "File owner (%o) is insecure",
+			             statbuf.st_mode);
+			return NULL;
 		}
 	}
 
-	/* Handle vpn secrets after the 'vpn' setting was read */
-	if (vpn_secrets) {
-		NMSettingVpn *s_vpn;
-
-		s_vpn = nm_connection_get_setting_vpn (connection);
-		if (s_vpn)
-			read_vpn_secrets (key_file, s_vpn);
-	}
+	key_file = g_key_file_new ();
+	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
+		goto out;
 
-	g_strfreev (groups);
+	connection = nm_keyfile_read (key_file, filename, NULL, _handler_read, NULL, error);
+	if (!connection)
+		goto out;
 
 	/* Normalize and verify the connection */
 	if (!nm_connection_normalize (connection, NULL, NULL, &verify_error)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			         "invalid connection: %s",
+		             "invalid connection: %s",
 		             verify_error->message);
 		g_clear_error (&verify_error);
 		g_object_unref (connection);
@@ -1364,3 +138,4 @@ out:
 	g_key_file_free (key_file);
 	return connection;
 }
+
diff --git a/src/settings/plugins/keyfile/tests/Makefile.in b/src/settings/plugins/keyfile/tests/Makefile.in
index c4d12459..a65110f6 100644
--- a/src/settings/plugins/keyfile/tests/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/Makefile.in
@@ -183,7 +183,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-	distdir
+	check recheck distdir
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
 # Read a list of newline-separated strings from the standard input,
 # and print each of them once, without duplicates.  Input order is
@@ -225,9 +225,190 @@ am__tty_colors = { \
     std=''; \
   fi; \
 }
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 DIST_SUBDIRS = keyfiles
 am__DIST_COMMON = $(srcdir)/Makefile.in \
-	$(top_srcdir)/build-aux/depcomp
+	$(top_srcdir)/build-aux/depcomp \
+	$(top_srcdir)/build-aux/test-driver
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -258,6 +439,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -371,6 +553,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
@@ -546,7 +729,7 @@ with_valgrind = @with_valgrind@
 all: all-recursive
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -769,98 +952,168 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
 	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	elif test -n "$$redo_logs"; then \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
 	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
 	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
 	    else \
-	      skipped="($$skip tests were not run)"; \
+	      color_start= color_end=; \
 	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test-keyfile.log: test-keyfile$(EXEEXT)
+	@p='test-keyfile$(EXEEXT)'; \
+	b='test-keyfile'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
 
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -943,6 +1196,9 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
@@ -1038,8 +1294,8 @@ uninstall-am:
 	install-strip installcheck installcheck-am installdirs \
 	installdirs-am maintainer-clean maintainer-clean-generic \
 	mostlyclean mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
-	uninstall-am
+	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+	uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
index f7a0c45e..de7af82b 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
@@ -135,6 +135,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -248,6 +249,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
index 9f4ef62f..62e6ae31 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
@@ -8,8 +8,8 @@ type=802-3-ethernet
 eap=tls;
 identity=Bill Smith
 ca-cert=48;130;2;52;48;130;1;161;2;16;2;173;102;126;78;69;254;94;87;111;60;152;25;94;221;192;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;30;23;13;57;52;49;49;48;57;48;48;48;48;48;48;90;23;13;49;48;48;49;48;55;50;51;53;57;53;57;90;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;129;155;48;13;6;9;42;134;72;134;247;13;1;1;1;5;0;3;129;137;0;48;129;133;2;126;0;146;206;122;193;174;131;62;90;170;137;131;87;172;37;1;118;12;173;174;142;44;55;206;235;53;120;100;84;3;229;132;64;81;201;191;143;8;226;138;130;8;210;22;134;55;85;233;177;33;2;173;118;104;129;154;5;162;75;201;75;37;102;34;86;108;136;7;143;247;129;89;109;132;7;101;112;19;113;118;62;155;119;76;227;80;137;86;152;72;185;29;167;41;26;19;46;74;17;89;156;30;21;213;73;84;44;115;58;105;130;177;151;57;156;109;112;103;72;229;221;45;214;200;30;123;2;3;1;0;1;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;3;126;0;101;221;126;225;178;236;176;226;58;224;236;113;70;154;25;17;184;211;199;160;180;3;64;38;2;62;9;156;225;18;179;209;90;246;55;165;183;97;3;182;91;22;105;59;198;68;8;12;136;83;12;107;151;73;199;62;53;220;108;185;187;170;223;92;187;58;47;147;96;182;169;75;77;242;32;247;205;95;127;100;123;142;220;0;92;215;250;119;202;57;22;89;111;14;234;211;181;131;127;77;77;66;86;118;180;201;95;4;248;56;248;235;210;95;117;95;205;123;252;229;142;128;124;252;80;
-client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
-private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+client-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+private-key=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
 private-key-password=12345testing
 
 [ipv4]
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
index 61afdd91..d3da598c 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
@@ -7,9 +7,9 @@ type=802-3-ethernet
 [802-1x]
 eap=tls;
 identity=Bill Smith
-ca-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;67;65;47;101;97;112;116;101;115;116;95;99;97;95;99;101;114;116;46;112;101;109;0;
-client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
-private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+ca-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;67;65;47;101;97;112;116;101;115;116;95;99;97;95;99;101;114;116;46;112;101;109;0;
+client-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+private-key=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
 private-key-password=12345testing
 
 [ipv4]
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index edece1b2..4fc44c7b 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -33,6 +33,7 @@
 
 #include "reader.h"
 #include "writer.h"
+#include "utils.h"
 
 #include "nm-test-utils.h"
 
@@ -99,33 +100,33 @@ test_read_valid_wired_connection (void)
 	const char *expected6_dnssearch3 = "gnu.org";
 
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.addresses1*semicolon at the end*");
+	                       "*ipv4.addresses:*semicolon at the end*addresses1*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.addresses2*semicolon at the end*");
+	                       "*ipv4.addresses:*semicolon at the end*addresses2*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*Missing prefix length*ipv4.address4*");
+	                       "*missing prefix length*address4*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*Missing prefix length*ipv4.address5*");
+	                       "*missing prefix length*address5*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.routes2*semicolon at the end*");
+	                       "*ipv4.routes*semicolon at the end*routes2*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.routes3*semicolon at the end*");
+	                       "*ipv4.routes*semicolon at the end*routes3*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.routes5*semicolon at the end*");
+	                       "*ipv4.routes*semicolon at the end*routes5*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.routes8*semicolon at the end*");
+	                       "*ipv4.routes*semicolon at the end*routes8*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*Missing prefix length*ipv6.address4*");
+	                       "*missing prefix length*address4*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv6.address5*semicolon at the end*");
+	                       "*ipv6.address*semicolon at the end*address5*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*Missing prefix length*ipv6.address5*");
+	                       "*missing prefix length*address5*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv6.address7*semicolon at the end*");
+	                       "*ipv6.address*semicolon at the end*address7*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv6.routes1*semicolon at the end*");
+	                       "*ipv6.routes*semicolon at the end*routes1*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv6.route6*semicolon at the end*");
+	                       "*ipv6.route*semicolon at the end*route6*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_FILE, NULL);
 	g_test_assert_expected_messages ();
 	ASSERT (connection != NULL,
@@ -772,11 +773,11 @@ test_read_wired_mac_case (void)
 	const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57";
 
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.addresses1*semicolon at the end*");
+	                       "*ipv4.addresses*semicolon at the end*addresses1*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv4.addresses2*semicolon at the end*");
+	                       "*ipv4.addresses*semicolon at the end*addresses2*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*ipv6.routes1*semicolon at the end*");
+	                       "*ipv6.routes*semicolon at the end*routes1*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_MAC_CASE_FILE, NULL);
 	g_test_assert_expected_messages ();
 	ASSERT (connection != NULL,
@@ -2126,6 +2127,10 @@ test_read_wired_8021x_tls_blob_connection (void)
 	gboolean success;
 	GBytes *blob;
 
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*<warn>  keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*<warn>  keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_BLOB_FILE, &error);
 	if (connection == NULL) {
 		g_assert (error);
@@ -2173,10 +2178,10 @@ test_read_wired_8021x_tls_blob_connection (void)
 	g_assert_cmpint (g_bytes_get_size (blob), ==, 568);
 
 	tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
-	g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem");
+	g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem");
 
 	tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
-	g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem");
+	g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem");
 
 	g_object_unref (connection);
 }
@@ -2258,6 +2263,12 @@ test_read_wired_8021x_tls_old_connection (void)
 	const char *tmp;
 	gboolean success;
 
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*<warn>  keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*<warn>  keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*<warn>  keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_OLD_FILE, &error);
 	if (connection == NULL) {
 		g_assert (error);
@@ -2291,13 +2302,13 @@ test_read_wired_8021x_tls_old_connection (void)
 	g_assert (g_strcmp0 (tmp, "12345testing") == 0);
 
 	tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
-	g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0);
+	g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0);
 
 	tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
-	g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/client.pem") == 0);
+	g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0);
 
 	tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
-	g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/client.pem") == 0);
+	g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0);
 
 	g_object_unref (connection);
 }
@@ -3566,10 +3577,61 @@ test_write_flags_property (void)
 	g_object_unref (connection);
 }
 
+/*****************************************************************************/
+
+static void
+_escape_filename (const char *filename, gboolean would_be_ignored)
+{
+	gs_free char *esc = NULL;
+
+	g_assert (filename && filename[0]);
+
+	if (!!would_be_ignored != !!nm_keyfile_plugin_utils_should_ignore_file (filename)) {
+		if (would_be_ignored)
+			g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename);
+		else
+			g_error ("We expect filename \"%s\" not to be ignored, but it is", filename);
+	}
+
+	esc = nm_keyfile_plugin_utils_escape_filename (filename);
+	g_assert (esc && esc[0]);
+	g_assert (!strchr (esc, '/'));
+
+	if (nm_keyfile_plugin_utils_should_ignore_file (esc))
+		g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc);
+}
+
+static void
+test_nm_keyfile_plugin_utils_escape_filename (void)
+{
+	_escape_filename ("ab", FALSE);
+	_escape_filename (".vim-file.swp", TRUE);
+	_escape_filename (".vim-file.Swp", TRUE);
+	_escape_filename (".vim-file.SWP", TRUE);
+	_escape_filename (".vim-file.swpx", TRUE);
+	_escape_filename (".vim-file.Swpx", TRUE);
+	_escape_filename (".vim-file.SWPX", TRUE);
+	_escape_filename (".pem-file.pem", TRUE);
+	_escape_filename (".pem-file.Pem", TRUE);
+	_escape_filename (".pem-file.PEM", TRUE);
+	_escape_filename (".pem-file.der", TRUE);
+	_escape_filename (".pem-file.Der", TRUE);
+	_escape_filename (".mkstemp.ABCEDF", TRUE);
+	_escape_filename (".mkstemp.abcdef", TRUE);
+	_escape_filename (".mkstemp.123456", TRUE);
+	_escape_filename (".mkstemp.A23456", TRUE);
+	_escape_filename (".#emacs-locking", TRUE);
+	_escape_filename ("file-with-tilde~", TRUE);
+	_escape_filename (".file-with-dot", TRUE);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
 {
+	_nm_utils_set_testing (NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK);
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
 	/* The tests */
@@ -3636,6 +3698,8 @@ int main (int argc, char **argv)
 	g_test_add_func ("/keyfile/test_read_flags_property ", test_read_flags_property);
 	g_test_add_func ("/keyfile/test_write_flags_property ", test_write_flags_property);
 
+	g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename ", test_nm_keyfile_plugin_utils_escape_filename);
+
 	return g_test_run ();
 }
 
diff --git a/src/settings/plugins/keyfile/utils.c b/src/settings/plugins/keyfile/utils.c
index 3361d554..4809c642 100644
--- a/src/settings/plugins/keyfile/utils.c
+++ b/src/settings/plugins/keyfile/utils.c
@@ -23,6 +23,7 @@
 #include <glib.h>
 #include <stdlib.h>
 #include <string.h>
+#include "gsystem-local-alloc.h"
 #include "utils.h"
 #include <nm-setting-wired.h>
 #include <nm-setting-wireless.h>
@@ -59,7 +60,7 @@ check_prefix (const char *base, const char *tag)
 
 	len = strlen (base);
 	tag_len = strlen (tag);
-	if ((len > tag_len) && !strncasecmp (base, tag, tag_len))
+	if ((len > tag_len) && !g_ascii_strncasecmp (base, tag, tag_len))
 		return TRUE;
 	return FALSE;
 }
@@ -74,7 +75,7 @@ check_suffix (const char *base, const char *tag)
 
 	len = strlen (base);
 	tag_len = strlen (tag);
-	if ((len > tag_len) && !strcasecmp (base + len - tag_len, tag))
+	if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag))
 		return TRUE;
 	return FALSE;
 }
@@ -87,199 +88,61 @@ check_suffix (const char *base, const char *tag)
 gboolean
 nm_keyfile_plugin_utils_should_ignore_file (const char *filename)
 {
-	char *base;
-	gboolean ignore = FALSE;
+	gs_free char *base = NULL;
 
 	g_return_val_if_fail (filename != NULL, TRUE);
 
 	base = g_path_get_basename (filename);
 	g_return_val_if_fail (base != NULL, TRUE);
 
-	/* Ignore files with certain patterns */
-	if (   (check_prefix (base, ".") && check_suffix (base, SWP_TAG))   /* vim temporary files: .filename.swp */
-	    || (check_prefix (base, ".") && check_suffix (base, SWPX_TAG))  /* vim temporary files: .filename.swpx */
-	    || check_suffix (base, PEM_TAG)                                 /* 802.1x certificates and keys */
-	    || check_suffix (base, DER_TAG)                                 /* 802.1x certificates and keys */
-	    || check_mkstemp_suffix (base)                                  /* temporary files created by mkstemp() */
-	    || check_prefix (base, ".#")                                    /* Emacs locking file (link) */
-	    || base[strlen (base) - 1] == '~')
-		ignore = TRUE;
+	/* Ignore hidden and backup files */
+	/* should_ignore_file() must mirror escape_filename() */
+	if (check_prefix (base, ".") || check_suffix (base, "~"))
+		return TRUE;
+	/* Ignore temporary files */
+	if (check_mkstemp_suffix (base))
+		return TRUE;
+	/* Ignore 802.1x certificates and keys */
+	if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG))
+		return TRUE;
 
-	g_free (base);
-	return ignore;
+	return FALSE;
 }
 
-typedef struct {
-	const char *setting;
-	const char *alias;
-} SettingAlias;
-
-static const SettingAlias alias_list[] = {
-	{ NM_SETTING_WIRED_SETTING_NAME, "ethernet" },
-	{ NM_SETTING_WIRELESS_SETTING_NAME, "wifi" },
-	{ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security" },
-};
-
-const char *
-nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name)
+char *
+nm_keyfile_plugin_utils_escape_filename (const char *filename)
 {
-	guint i;
+	GString *str;
+	const char *f = filename;
+	const char ESCAPE_CHAR = '*';
 
-	g_return_val_if_fail (setting_name != NULL, NULL);
+	/* keyfile used to escape with '*', do not change that behavior.
+	 * But for newly added escapings, use '_' instead. */
+	const char ESCAPE_CHAR2 = '_';
 
-	for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
-		if (strcmp (setting_name, alias_list[i].setting) == 0)
-			return alias_list[i].alias;
-	}
-	return NULL;
-}
+	g_return_val_if_fail (filename && filename[0], NULL);
 
-const char *
-nm_keyfile_plugin_get_setting_name_for_alias (const char *alias)
-{
-	guint i;
-
-	g_return_val_if_fail (alias != NULL, NULL);
+	str = g_string_sized_new (60);
 
-	for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
-		if (strcmp (alias, alias_list[i].alias) == 0)
-			return alias_list[i].setting;
+	/* Convert '/' to ESCAPE_CHAR */
+	for (f = filename; f[0]; f++) {
+		if (f[0] == '/')
+			g_string_append_c (str, ESCAPE_CHAR);
+		else
+			g_string_append_c (str, f[0]);
 	}
-	return NULL;
-}
-
-/**********************************************************************/
-
-/* List helpers */
-#define DEFINE_KF_LIST_WRAPPER(stype, get_ctype, set_ctype) \
-get_ctype \
-nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
-                                         const char *group, \
-                                         const char *key, \
-                                         gsize *out_length, \
-                                         GError **error) \
-{ \
-	get_ctype list; \
-	const char *alias; \
-	GError *local = NULL; \
- \
-	list = g_key_file_get_##stype##_list (kf, group, key, out_length, &local); \
-	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
-		alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
-		if (alias) { \
-			g_clear_error (&local); \
-			list = g_key_file_get_##stype##_list (kf, alias, key, out_length, &local); \
-		} \
-	} \
-	if (local) \
-		g_propagate_error (error, local); \
-	return list; \
-} \
- \
-void \
-nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
-                                         const char *group, \
-                                         const char *key, \
-                                         set_ctype list[], \
-                                         gsize length) \
-{ \
-	const char *alias; \
- \
-	alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
-	g_key_file_set_##stype##_list (kf, alias ? alias : group, key, list, length); \
-}
-
-DEFINE_KF_LIST_WRAPPER(integer, gint*, gint);
-DEFINE_KF_LIST_WRAPPER(string, gchar **, const gchar* const);
 
-/* Single value helpers */
-#define DEFINE_KF_WRAPPER(stype, get_ctype, set_ctype) \
-get_ctype \
-nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
-                                  const char *group, \
-                                  const char *key, \
-                                  GError **error) \
-{ \
-	get_ctype val; \
-	const char *alias; \
-	GError *local = NULL; \
- \
-	val = g_key_file_get_##stype (kf, group, key, &local); \
-	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
-		alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
-		if (alias) { \
-			g_clear_error (&local); \
-			val = g_key_file_get_##stype (kf, alias, key, &local); \
-		} \
-	} \
-	if (local) \
-		g_propagate_error (error, local); \
-	return val; \
-} \
- \
-void \
-nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
-                                  const char *group, \
-                                  const char *key, \
-                                  set_ctype value) \
-{ \
-	const char *alias; \
- \
-	alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
-	g_key_file_set_##stype (kf, alias ? alias : group, key, value); \
+	/* escape_filename() must avoid anything that should_ignore_file() would reject.
+	 * We can escape here more aggressivly then what we would read back. */
+	if (check_prefix (str->str, "."))
+		str->str[0] = ESCAPE_CHAR2;
+	if (check_suffix (str->str, "~"))
+		str->str[str->len - 1] = ESCAPE_CHAR2;
+	if (   check_mkstemp_suffix (str->str)
+	    || check_suffix (str->str, PEM_TAG)
+	    || check_suffix (str->str, DER_TAG))
+		g_string_append_c (str, ESCAPE_CHAR2);
+
+	return g_string_free (str, FALSE);;
 }
 
-DEFINE_KF_WRAPPER(string, gchar*, const gchar*);
-DEFINE_KF_WRAPPER(integer, gint, gint);
-DEFINE_KF_WRAPPER(uint64, guint64, guint64);
-DEFINE_KF_WRAPPER(boolean, gboolean, gboolean);
-DEFINE_KF_WRAPPER(value, gchar*, const gchar*);
-
-
-gchar **
-nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
-                               const char *group,
-                               gsize *out_length,
-                               GError **error)
-{
-	gchar **keys;
-	const char *alias;
-	GError *local = NULL;
-
-	keys = g_key_file_get_keys (kf, group, out_length, &local);
-	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
-		alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
-		if (alias) {
-			g_clear_error (&local);
-			keys = g_key_file_get_keys (kf, alias, out_length, &local);
-		}
-	}
-	if (local)
-		g_propagate_error (error, local);
-	return keys;
-}
-
-gboolean
-nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
-                              const char *group,
-                              const char *key,
-                              GError **error)
-{
-	gboolean has;
-	const char *alias;
-	GError *local = NULL;
-
-	has = g_key_file_has_key (kf, group, key, &local);
-	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
-		alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
-		if (alias) {
-			g_clear_error (&local);
-			has = g_key_file_has_key (kf, alias, key, &local);
-		}
-	}
-	if (local)
-		g_propagate_error (error, local);
-	return has;
-}
-
-
diff --git a/src/settings/plugins/keyfile/utils.h b/src/settings/plugins/keyfile/utils.h
index d1533671..d0862284 100644
--- a/src/settings/plugins/keyfile/utils.h
+++ b/src/settings/plugins/keyfile/utils.h
@@ -22,7 +22,6 @@
 #define _UTILS_H_
 
 #include <glib.h>
-#include "common.h"
 #include "NetworkManagerUtils.h"
 
 #define NM_KEYFILE_CONNECTION_LOG_PATH(path)  str_if_set (path,"in-memory")
@@ -33,55 +32,7 @@
 
 gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename);
 
-const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);
-
-const char *nm_keyfile_plugin_get_setting_name_for_alias (const char *alias);
-
-/*********************************************************/
-
-/* List helpers */
-#define DEFINE_KF_LIST_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
-get_ctype nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
-                                                   const char *group, \
-                                                   const char *key, \
-                                                   gsize *out_length, \
-                                                   GError **error); \
-\
-void nm_keyfile_plugin_kf_set_##stype##_list  (GKeyFile *kf, \
-                                               const char *group, \
-                                               const char *key, \
-                                               set_ctype list[], \
-                                               gsize length);
-DEFINE_KF_LIST_WRAPPER_PROTO(integer, gint*, gint)
-DEFINE_KF_LIST_WRAPPER_PROTO(string, gchar**, const gchar* const)
-
-/* Single-value helpers */
-#define DEFINE_KF_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
-get_ctype nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
-                                            const char *group, \
-                                            const char *key, \
-                                            GError **error); \
-\
-void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
-                                       const char *group, \
-                                       const char *key, \
-                                       set_ctype value);
-DEFINE_KF_WRAPPER_PROTO(string, gchar*, const gchar*)
-DEFINE_KF_WRAPPER_PROTO(integer, gint, gint)
-DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64)
-DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean)
-DEFINE_KF_WRAPPER_PROTO(value, gchar*, const gchar*)
-
-/* Misc */
-gchar ** nm_keyfile_plugin_kf_get_keys    (GKeyFile *kf,
-                                           const char *group,
-                                           gsize *out_length,
-                                           GError **error);
-
-gboolean nm_keyfile_plugin_kf_has_key     (GKeyFile *kf,
-                                           const char *group,
-                                           const char *key,
-                                           GError **error);
+char *nm_keyfile_plugin_utils_escape_filename (const char *filename);
 
 #endif  /* _UTILS_H_ */
 
diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c
index 7aa5bfb0..b6a8786d 100644
--- a/src/settings/plugins/keyfile/writer.c
+++ b/src/settings/plugins/keyfile/writer.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
+ * Copyright (C) 2008 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
@@ -24,407 +24,22 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <stdio.h>
 #include <errno.h>
-
-#include <nm-setting.h>
-#include <nm-setting-connection.h>
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-ip6-config.h>
-#include <nm-setting-vpn.h>
-#include <nm-setting-wired.h>
-#include <nm-setting-wireless.h>
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-bluetooth.h>
-#include <nm-setting-8021x.h>
-#include <nm-utils.h>
 #include <string.h>
-#include <arpa/inet.h>
 
 #include "nm-glib-compat.h"
+
 #include "nm-logging.h"
 #include "writer.h"
 #include "common.h"
 #include "utils.h"
+#include "nm-keyfile-internal.h"
 
-/* Some setting properties also contain setting names, such as
- * NMSettingConnection's 'type' property (which specifies the base type of the
- * connection, eg ethernet or wifi) or the 802-11-wireless setting's
- * 'security' property which specifies whether or not the AP requires
- * encrpytion.  This function handles translating those properties' values
- * from the real setting name to the more-readable alias.
- */
-static void
-setting_alias_writer (GKeyFile *file,
-                      const char *keyfile_dir,
-                      const char *uuid,
-                      NMSetting *setting,
-                      const char *key,
-                      const GValue *value)
-{
-	const char *str, *alias;
-
-	str = g_value_get_string (value);
-	alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
-	nm_keyfile_plugin_kf_set_string (file,
-	                                 nm_setting_get_name (setting),
-	                                 key,
-	                                 alias ? alias : str);
-}
-
-static gboolean
-write_array_of_uint (GKeyFile *file,
-                     NMSetting *setting,
-                     const char *key,
-                     const GValue *value)
-{
-	GArray *array;
-	int i;
-	int *tmp_array;
-
-	array = (GArray *) g_value_get_boxed (value);
-	if (!array || !array->len)
-		return TRUE;
-
-	tmp_array = g_new (gint, array->len);
-	for (i = 0; i < array->len; i++)
-		tmp_array[i] = g_array_index (array, int, i);
-
-	nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
-	g_free (tmp_array);
-	return TRUE;
-}
-
-static void
-dns_writer (GKeyFile *file,
-            const char *keyfile_dir,
-            const char *uuid,
-            NMSetting *setting,
-            const char *key,
-            const GValue *value)
-{
-	char **list;
-
-	list = g_value_get_boxed (value);
-	if (list && list[0]) {
-		nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key,
-		                                      (const char **) list, g_strv_length (list));
-	}
-}
-
-static void
-write_ip_values (GKeyFile *file,
-                 const char *setting_name,
-                 GPtrArray *array,
-                 const char *gateway,
-                 gboolean is_route)
-{
-	GString *output;
-	int family, i;
-	const char *addr, *gw;
-	guint32 plen, metric;
-	char key_name[30], *key_name_idx;
-
-	if (!array->len)
-		return;
-
-	family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
-
-	strcpy (key_name, is_route ? "route" : "address");
-	key_name_idx = key_name + strlen (key_name);
-
-	output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
-	for (i = 0; i < array->len; i++) {
-		if (is_route) {
-			NMIPRoute *route = array->pdata[i];
-
-			addr = nm_ip_route_get_dest (route);
-			plen = nm_ip_route_get_prefix (route);
-			gw = nm_ip_route_get_next_hop (route);
-			metric = MAX (0, nm_ip_route_get_metric (route));
-		} else {
-			NMIPAddress *address = array->pdata[i];
-
-			addr = nm_ip_address_get_address (address);
-			plen = nm_ip_address_get_prefix (address);
-			gw = i == 0 ? gateway : NULL;
-			metric = 0;
-		}
-
-		g_string_set_size (output, 0);
-		g_string_append_printf (output, "%s/%u", addr, plen);
-		if (metric || gw) {
-			/* Older versions of the plugin do not support the form
-			 * "a.b.c.d/plen,,metric", so, we always have to write the
-			 * gateway, even if there isn't one.
-			 * The current version supports reading of the above form.
-			 */
-			if (!gw) {
-				if (family == AF_INET)
-					gw = "0.0.0.0";
-				else
-					gw = "::";
-			}
-
-			g_string_append_printf (output, ",%s", gw);
-			if (metric)
-				g_string_append_printf (output, ",%lu", (unsigned long) metric);
-		}
-
-		sprintf (key_name_idx, "%d", i + 1);
-		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
-	}
-	g_string_free (output, TRUE);
-}
-
-static void
-addr_writer (GKeyFile *file,
-             const char *keyfile_dir,
-             const char *uuid,
-             NMSetting *setting,
-             const char *key,
-             const GValue *value)
-{
-	GPtrArray *array;
-	const char *setting_name = nm_setting_get_name (setting);
-	const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting));
-
-	array = (GPtrArray *) g_value_get_boxed (value);
-	if (array && array->len)
-		write_ip_values (file, setting_name, array, gateway, FALSE);
-}
-
-static void
-ip4_addr_label_writer (GKeyFile *file,
-                       const char *keyfile_dir,
-                       const char *uuid,
-                       NMSetting *setting,
-                       const char *key,
-                       const GValue *value)
-{
-	/* skip */
-}
-
-static void
-gateway_writer (GKeyFile *file,
-                const char *keyfile_dir,
-                const char *uuid,
-                NMSetting *setting,
-                const char *key,
-                const GValue *value)
-{
-	/* skip */
-}
-
-static void
-route_writer (GKeyFile *file,
-              const char *keyfile_dir,
-              const char *uuid,
-              NMSetting *setting,
-              const char *key,
-              const GValue *value)
-{
-	GPtrArray *array;
-	const char *setting_name = nm_setting_get_name (setting);
-
-	array = (GPtrArray *) g_value_get_boxed (value);
-	if (array && array->len)
-		write_ip_values (file, setting_name, array, NULL, TRUE);
-}
-
-static void
-write_hash_of_string (GKeyFile *file,
-                      NMSetting *setting,
-                      const char *key,
-                      const GValue *value)
-{
-	GHashTableIter iter;
-	const char *property = NULL, *data = NULL;
-	const char *group_name = nm_setting_get_name (setting);
-	gboolean vpn_secrets = FALSE;
-
-	/* Write VPN secrets out to a different group to keep them separate */
-	if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
-		group_name = VPN_SECRETS_GROUP;
-		vpn_secrets = TRUE;
-	}
-
-	g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
-	while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) {
-		gboolean write_item = TRUE;
-
-		/* Handle VPN secrets specially; they are nested in the property's hash;
-		 * we don't want to write them if the secret is not saved, not required,
-		 * or owned by a user's secret agent.
-		 */
-		if (vpn_secrets) {
-			NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-			nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
-			if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
-				write_item = FALSE;
-		}
-
-		if (write_item)
-			nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
-	}
-}
 
-static void
-ssid_writer (GKeyFile *file,
-             const char *keyfile_dir,
-             const char *uuid,
-             NMSetting *setting,
-             const char *key,
-             const GValue *value)
-{
-	GBytes *bytes;
-	const guint8 *ssid_data;
-	gsize ssid_len;
-	const char *setting_name = nm_setting_get_name (setting);
-	gboolean new_format = TRUE;
-	unsigned int semicolons = 0;
-	int i, *tmp_array;
-	char *ssid;
-
-	g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
-
-	bytes = g_value_get_boxed (value);
-	if (!bytes)
-		return;
-	ssid_data = g_bytes_get_data (bytes, &ssid_len);
-	if (ssid_len == 0)
-		return;
-
-	/* Check whether each byte is printable.  If not, we have to use an
-	 * integer list, otherwise we can just use a string.
-	 */
-	for (i = 0; i < ssid_len; i++) {
-		char c = ssid_data[i] & 0xFF;
-		if (!g_ascii_isprint (c)) {
-			new_format = FALSE;
-			break;
-		}
-		if (c == ';')
-			semicolons++;
-	}
-
-	if (new_format) {
-		ssid = g_malloc0 (ssid_len + semicolons + 1);
-		if (semicolons == 0)
-			memcpy (ssid, ssid_data, ssid_len);
-		else {
-			/* Escape semicolons with backslashes to make strings
-			 * containing ';', such as '16;17;' unambiguous */
-			int j = 0;
-			for (i = 0; i < ssid_len; i++) {
-				if (ssid_data[i] == ';')
-					ssid[j++] = '\\';
-				ssid[j++] = ssid_data[i];
-			}
-		}
-		nm_keyfile_plugin_kf_set_string (file, setting_name, key, ssid);
-		g_free (ssid);
-	} else {
-		tmp_array = g_new (gint, ssid_len);
-		for (i = 0; i < ssid_len; i++)
-			tmp_array[i] = (int) ssid_data[i];
-		nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, ssid_len);
-		g_free (tmp_array);
-	}
-}
-
-static void
-password_raw_writer (GKeyFile *file,
-                     const char *keyfile_dir,
-                     const char *uuid,
-                     NMSetting *setting,
-                     const char *key,
-                     const GValue *value)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GBytes *array;
-	int *tmp_array;
-	gsize i, len;
-	const char *data;
-
-	g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
-
-	array = (GBytes *) g_value_get_boxed (value);
-	if (!array)
-		return;
-	data = g_bytes_get_data (array, &len);
-	if (!data || !len)
-		return;
-
-	tmp_array = g_new (gint, len);
-	for (i = 0; i < len; i++)
-		tmp_array[i] = (int) data[i];
-	nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, len);
-	g_free (tmp_array);
-}
+typedef struct {
+	const char *keyfile_dir;
+} WriteInfo;
 
-typedef struct ObjectType {
-	const char *key;
-	const char *suffix;
-	const char *privkey_pw_prop;
-	NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
-	NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
-	const char *           (*path_func)   (NMSetting8021x *setting);
-	GBytes *               (*blob_func)   (NMSetting8021x *setting);
-} ObjectType;
-
-static const ObjectType objtypes[10] = {
-	{ NM_SETTING_802_1X_CA_CERT,
-	  "ca-cert",
-	  NULL,
-	  nm_setting_802_1x_get_ca_cert_scheme,
-	  NULL,
-	  nm_setting_802_1x_get_ca_cert_path,
-	  nm_setting_802_1x_get_ca_cert_blob },
-
-	{ NM_SETTING_802_1X_PHASE2_CA_CERT,
-	  "inner-ca-cert",
-	  NULL,
-	  nm_setting_802_1x_get_phase2_ca_cert_scheme,
-	  NULL,
-	  nm_setting_802_1x_get_phase2_ca_cert_path,
-	  nm_setting_802_1x_get_phase2_ca_cert_blob },
-
-	{ NM_SETTING_802_1X_CLIENT_CERT,
-	  "client-cert",
-	  NULL,
-	  nm_setting_802_1x_get_client_cert_scheme,
-	  NULL,
-	  nm_setting_802_1x_get_client_cert_path,
-	  nm_setting_802_1x_get_client_cert_blob },
-
-	{ NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	  "inner-client-cert",
-	  NULL,
-	  nm_setting_802_1x_get_phase2_client_cert_scheme,
-	  NULL,
-	  nm_setting_802_1x_get_phase2_client_cert_path,
-	  nm_setting_802_1x_get_phase2_client_cert_blob },
-
-	{ NM_SETTING_802_1X_PRIVATE_KEY,
-	  "private-key",
-	  NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
-	  nm_setting_802_1x_get_private_key_scheme,
-	  nm_setting_802_1x_get_private_key_format,
-	  nm_setting_802_1x_get_private_key_path,
-	  nm_setting_802_1x_get_private_key_blob },
-
-	{ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	  "inner-private-key",
-	  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
-	  nm_setting_802_1x_get_phase2_private_key_scheme,
-	  nm_setting_802_1x_get_phase2_private_key_format,
-	  nm_setting_802_1x_get_phase2_private_key_path,
-	  nm_setting_802_1x_get_phase2_private_key_blob },
-
-	{ NULL },
-};
 
 static gboolean
 write_cert_key_file (const char *path,
@@ -490,61 +105,75 @@ out:
 }
 
 static void
-cert_writer (GKeyFile *file,
-             const char *keyfile_dir,
-             const char *uuid,
-             NMSetting *setting,
-             const char *key,
-             const GValue *value)
+cert_writer (NMConnection *connection,
+             GKeyFile *file,
+             NMKeyfileWriteTypeDataCert *cert_data,
+             WriteInfo *info,
+             GError **error)
 {
-	const char *setting_name = nm_setting_get_name (setting);
+	const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
 	NMSetting8021xCKScheme scheme;
 	NMSetting8021xCKFormat format;
 	const char *path = NULL, *ext = "pem";
-	const ObjectType *objtype = NULL;
-	int i;
 
-	for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) {
-		if (g_strcmp0 (objtypes[i].key, key) == 0) {
-			objtype = &objtypes[i];
-			break;
-		}
-	}
-	if (!objtype) {
-		g_return_if_fail (objtype);
-		return;
-	}
-
-	scheme = objtype->scheme_func (NM_SETTING_802_1X (setting));
+	scheme = cert_data->scheme_func (cert_data->setting);
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
-		path = objtype->path_func (NM_SETTING_802_1X (setting));
+		char *tmp = NULL;
+		const char *accepted_path = NULL;
+
+		path = cert_data->path_func (cert_data->setting);
 		g_assert (path);
 
-		/* If the path is rooted in the keyfile directory, just use a
-		 * relative path instead of an absolute one.
-		 */
-		if (g_str_has_prefix (path, keyfile_dir)) {
-			path += strlen (keyfile_dir);
-			while (*path == '/')
-				path++;
+		if (g_str_has_prefix (path, info->keyfile_dir)) {
+			const char *p = path + strlen (info->keyfile_dir);
+
+			/* If the path is rooted in the keyfile directory, just use a
+			 * relative path instead of an absolute one.
+			 */
+			if (*p == '/') {
+				while (*p == '/')
+					p++;
+				if (p[0]) {
+					/* If @p looks like an integer list, the following detection will fail too and
+					 * we will file:// qualify the path below. We thus avoid writing a path string
+					 * that would be interpreted as legacy binary format by reader. */
+					tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, p, -1, FALSE, NULL);
+					if (tmp) {
+						g_clear_pointer (&tmp, g_free);
+						accepted_path = p;
+					}
+				}
+			}
+		}
+		if (!accepted_path) {
+			/* What we are about to write, must also be understood by the reader.
+			 * Otherwise, add a file:// prefix */
+			tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, path, -1, FALSE, NULL);
+			if (tmp) {
+				g_clear_pointer (&tmp, g_free);
+				accepted_path = path;
+			}
 		}
 
-		nm_keyfile_plugin_kf_set_string (file, setting_name, key, path);
+		if (!accepted_path)
+			accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path);
+		g_free (tmp);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		GBytes *blob;
 		const guint8 *blob_data;
 		gsize blob_len;
 		gboolean success;
-		GError *error = NULL;
+		GError *local = NULL;
 		char *new_path;
 
-		blob = objtype->blob_func (NM_SETTING_802_1X (setting));
+		blob = cert_data->blob_func (cert_data->setting);
 		g_assert (blob);
 		blob_data = g_bytes_get_data (blob, &blob_len);
 
-		if (objtype->format_func) {
+		if (cert_data->format_func) {
 			/* Get the extension for a private key */
-			format = objtype->format_func (NM_SETTING_802_1X (setting));
+			format = cert_data->format_func (cert_data->setting);
 			if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
 				ext = "p12";
 		} else {
@@ -556,246 +185,46 @@ cert_writer (GKeyFile *file,
 		/* Write the raw data out to the standard file so that we can use paths
 		 * from now on instead of pushing around the certificate data.
 		 */
-		new_path = g_strdup_printf ("%s/%s-%s.%s", keyfile_dir, uuid, objtype->suffix, ext);
-		g_assert (new_path);
+		new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection),
+		                            cert_data->suffix, ext);
 
-		success = write_cert_key_file (new_path, blob_data, blob_len, &error);
+		success = write_cert_key_file (new_path, blob_data, blob_len, &local);
 		if (success) {
-			/* Write the path value to the keyfile */
-			nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path);
+			/* Write the path value to the keyfile.
+			 * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64,"  */
+			nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, strrchr (new_path, '/') + 1);
 		} else {
-			nm_log_warn (LOGD_SETTINGS, "Failed to write certificate/key %s: %s",
-			             new_path, error->message);
-			g_error_free (error);
+			nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: failed to write certificate to file %s: %s",
+			             setting_name, cert_data->property_name, new_path, local->message);
+			g_error_free (local);
 		}
 		g_free (new_path);
-	} else
-		g_assert_not_reached ();
-}
-
-typedef struct {
-	const char *setting_name;
-	const char *key;
-	void (*writer) (GKeyFile *keyfile,
-	                const char *keyfile_dir,
-	                const char *uuid,
-	                NMSetting *setting,
-	                const char *key,
-	                const GValue *value);
-} KeyWriter;
-
-/* A table of keys that require further parsing/conversion because they are
- * stored in a format that can't be automatically read using the key's type.
- * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
- * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored 
- * in struct in6_addr internally, but as string in keyfiles.
- */
-static KeyWriter key_writers[] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME,
-	  NM_SETTING_CONNECTION_TYPE,
-	  setting_alias_writer },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ADDRESSES,
-	  addr_writer },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  "address-labels",
-	  ip4_addr_label_writer },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ADDRESSES,
-	  addr_writer },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_GATEWAY,
-	  gateway_writer },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_GATEWAY,
-	  gateway_writer },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ROUTES,
-	  route_writer },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_ROUTES,
-	  route_writer },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_DNS,
-	  dns_writer },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	  NM_SETTING_IP_CONFIG_DNS,
-	  dns_writer },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,
-	  NM_SETTING_WIRELESS_SSID,
-	  ssid_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PASSWORD_RAW,
-	  password_raw_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_CA_CERT,
-	  cert_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_CLIENT_CERT,
-	  cert_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PRIVATE_KEY,
-	  cert_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PHASE2_CA_CERT,
-	  cert_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	  cert_writer },
-	{ NM_SETTING_802_1X_SETTING_NAME,
-	  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	  cert_writer },
-	{ NULL, NULL, NULL }
-};
-
-typedef struct {
-	GKeyFile *keyfile;
-	const char *keyfile_dir;
-	const char *uuid;
-} WriteInfo;
-
-static void
-write_setting_value (NMSetting *setting,
-                     const char *key,
-                     const GValue *value,
-                     GParamFlags flag,
-                     gpointer user_data)
-{
-	WriteInfo *info = user_data;
-	const char *setting_name;
-	GType type = G_VALUE_TYPE (value);
-	KeyWriter *writer = &key_writers[0];
-	GParamSpec *pspec;
-
-	/* Setting name gets picked up from the keyfile's section name instead */
-	if (!strcmp (key, NM_SETTING_NAME))
-		return;
-
-	/* Don't write the NMSettingConnection object's 'read-only' property */
-	if (   NM_IS_SETTING_CONNECTION (setting)
-	    && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
-		return;
-
-	setting_name = nm_setting_get_name (setting);
-
-	/* If the value is the default value, remove the item from the keyfile */
-	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
-	if (pspec) {
-		if (g_param_value_defaults (pspec, (GValue *) value)) {
-			g_key_file_remove_key (info->keyfile, setting_name, key, NULL);
-			return;
-		}
-	}
-
-	/* Don't write secrets that are owned by user secret agents or aren't
-	 * supposed to be saved.  VPN secrets are handled specially though since
-	 * the secret flags there are in a third-level hash in the 'secrets'
-	 * property.
-	 */
-	if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) {
-		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-		if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
-			g_assert_not_reached ();
-		if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
-			return;
-	}
-
-	/* Look through the list of handlers for non-standard format key values */
-	while (writer->setting_name) {
-		if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) {
-			(*writer->writer) (info->keyfile, info->keyfile_dir, info->uuid, setting, key, value);
-			return;
-		}
-		writer++;
-	}
-
-	if (type == G_TYPE_STRING) {
-		const char *str;
-
-		str = g_value_get_string (value);
-		if (str)
-			nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
-	} else if (type == G_TYPE_UINT)
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
-	else if (type == G_TYPE_INT)
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
-	else if (type == G_TYPE_UINT64) {
-		char *numstr;
-
-		numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
-		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
-		g_free (numstr);
-	} else if (type == G_TYPE_INT64) {
-		char *numstr;
-
-		numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
-		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
-		g_free (numstr);
-	} else if (type == G_TYPE_BOOLEAN) {
-		nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
-	} else if (type == G_TYPE_CHAR) {
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
-	} else if (type == G_TYPE_BYTES) {
-		GBytes *bytes;
-		const guint8 *data;
-		gsize len = 0;
-
-		bytes = g_value_get_boxed (value);
-		data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
-
-		if (data != NULL && len > 0) {
-			int *tmp_array;
-			int i;
-
-			tmp_array = g_new (gint, len);
-			for (i = 0; i < len; i++)
-				tmp_array[i] = (int) data[i];
-
-			nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len);
-			g_free (tmp_array);
-		}
-	} else if (type == G_TYPE_STRV) {
-		char **array;
-
-		array = (char **) g_value_get_boxed (value);
-		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
-	} else if (type == G_TYPE_HASH_TABLE) {
-		write_hash_of_string (info->keyfile, setting, key, value);
-	} else if (type == G_TYPE_ARRAY) {
-		if (!write_array_of_uint (info->keyfile, setting, key, value)) {
-			nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", 
-			             setting_name, key, g_type_name (type));
-		}
-	} else if (G_VALUE_HOLDS_FLAGS (value)) {
-		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
-		nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value));
-	} else if (G_VALUE_HOLDS_ENUM (value))
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value));
-	else {
-		nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", 
-		             setting_name, key, g_type_name (type));
+	} else {
+		/* scheme_func() returns UNKNOWN in all other cases. The only valid case
+		 * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this
+		 * case, we don't expect the writer to be called, because the default value
+		 * will not be serialized.
+		 * The only other reason for the scheme to be UNKNOWN is an invalid cert.
+		 * But our connection verifies, so that cannot happen either. */
+		g_return_if_reached ();
 	}
 }
 
-static char *
-_writer_id_to_filename (const char *id)
+static gboolean
+_handler_write (NMConnection *connection,
+                GKeyFile *keyfile,
+                NMKeyfileWriteType type,
+                void *type_data,
+                void *user_data,
+                GError **error)
 {
-	char *filename, *f;
-	const char *i = id;
-
-	f = filename = g_malloc0 (strlen (id) + 1);
-
-	/* Convert '/' to '*' */
-	while (*i) {
-		if (*i == '/')
-			*f++ = '*';
-		else
-			*f++ = *i;
-		i++;
+	if (type == NM_KEYFILE_WRITE_TYPE_CERT) {
+		cert_writer (connection, keyfile,
+		             (NMKeyfileWriteTypeDataCert *) type_data,
+		             (WriteInfo *) user_data, error);
+		return TRUE;
 	}
-
-	return filename;
+	return FALSE;
 }
 
 static gboolean
@@ -811,12 +240,13 @@ _internal_write_connection (NMConnection *connection,
 	char *data;
 	gsize len;
 	gboolean success = FALSE;
-	char *filename = NULL, *path;
+	char *path;
 	const char *id;
-	WriteInfo info;
+	WriteInfo info = { 0 };
 	GError *local_err = NULL;
 
 	g_return_val_if_fail (!out_path || !*out_path, FALSE);
+	g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
 
 	if (!nm_connection_verify (connection, error))
 		g_return_val_if_reached (FALSE);
@@ -824,14 +254,15 @@ _internal_write_connection (NMConnection *connection,
 	id = nm_connection_get_id (connection);
 	g_assert (id && *id);
 
-	info.keyfile = key_file = g_key_file_new ();
 	info.keyfile_dir = keyfile_dir;
-	info.uuid = nm_connection_get_uuid (connection);
-	g_assert (info.uuid);
-	nm_connection_for_each_setting_value (connection, write_setting_value, &info);
+
+	key_file = nm_keyfile_write (connection, _handler_write, &info, error);
+	if (!key_file)
+		return FALSE;
 	data = g_key_file_to_data (key_file, &len, error);
+	g_key_file_unref (key_file);
 	if (!data)
-		goto out;
+		return FALSE;
 
 	/* If we have existing file path, use it. Else generate one from
 	 * connection's ID.
@@ -839,8 +270,10 @@ _internal_write_connection (NMConnection *connection,
 	if (existing_path != NULL) {
 		path = g_strdup (existing_path);
 	} else {
-		filename = _writer_id_to_filename (id);
-		path = g_build_filename (keyfile_dir, filename, NULL);
+		char *filename_escaped = nm_keyfile_plugin_utils_escape_filename (id);
+
+		path = g_build_filename (keyfile_dir, filename_escaped, NULL);
+		g_free (filename_escaped);
 	}
 
 	/* If a file with this path already exists (but isn't the existing path
@@ -850,24 +283,42 @@ _internal_write_connection (NMConnection *connection,
 	 * there's a race here, but there's not a lot we can do about it, and
 	 * we shouldn't get more than one connection with the same UUID either.
 	 */
-	if (g_file_test (path, G_FILE_TEST_EXISTS) && (g_strcmp0 (path, existing_path) != 0)) {
-		/* A keyfile with this connection's ID already exists. Pick another name. */
-		g_free (path);
+	if (g_strcmp0 (path, existing_path) != 0 && g_file_test (path, G_FILE_TEST_EXISTS)) {
+		guint i;
+		gboolean name_found = FALSE;
 
-		path = g_strdup_printf ("%s/%s-%s", keyfile_dir, filename, nm_connection_get_uuid (connection));
-		if (g_file_test (path, G_FILE_TEST_EXISTS)) {
-			if (existing_path == NULL || g_strcmp0 (path, existing_path) != 0) {
-				/* This should not happen. But, it actually occurs when
-				 * two connections have the same UUID, and one of the connections
-				 * is edited to contain the same ID as the other one.
-				 * Give up.
-				 */
+		/* A keyfile with this connection's ID already exists. Pick another name. */
+		for (i = 0; i < 100; i++) {
+			char *filename, *filename_escaped;
+
+			if (i == 0)
+				filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection));
+			else
+				filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i);
+
+			filename_escaped = nm_keyfile_plugin_utils_escape_filename (filename);
+
+			g_free (path);
+			path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped);
+			g_free (filename);
+			g_free (filename_escaped);
+			if (g_strcmp0 (path, existing_path) == 0 || !g_file_test (path, G_FILE_TEST_EXISTS)) {
+				name_found = TRUE;
+				break;
+			}
+		}
+		if (!name_found) {
+			if (existing_path == NULL) {
+				/* this really should not happen, we tried hard to find an unused name... bail out. */
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-				                    "%s.%d: could not find suitable keyfile file name (%s already used)",
-				                    __FILE__, __LINE__, path);
+				                    "could not find suitable keyfile file name (%s already used)", path);
 				g_free (path);
 				goto out;
 			}
+			/* Both our preferred path based on connection id and id-uuid are taken.
+			 * Fallback to @existing_path */
+			g_free (path);
+			path = g_strdup (existing_path);
 		}
 	}
 
@@ -909,9 +360,7 @@ _internal_write_connection (NMConnection *connection,
 	g_free (path);
 
 out:
-	g_free (filename);
 	g_free (data);
-	g_key_file_free (key_file);
 	return success;
 }
 
diff --git a/src/settings/plugins/keyfile/writer.h b/src/settings/plugins/keyfile/writer.h
index a602f2f4..8b08812d 100644
--- a/src/settings/plugins/keyfile/writer.h
+++ b/src/settings/plugins/keyfile/writer.h
@@ -22,7 +22,6 @@
 #ifndef _KEYFILE_PLUGIN_WRITER_H
 #define _KEYFILE_PLUGIN_WRITER_H
 
-#include <sys/types.h>
 #include <glib.h>
 #include <nm-connection.h>