summary refs log tree commit diff
path: root/src/NetworkManagerUtils.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-03-01 16:55:22 +0100
committerMichael Biebl <biebl@debian.org>2016-03-01 16:55:22 +0100
commitc2de0d98ba39e0a1a970d066fd19be786092f376 (patch)
tree3838363c06a6019db6cf1f882ea34ebded63c38b /src/NetworkManagerUtils.c
parent494f296a3baab08522617b24b1f126d8f9a17502 (diff)
Imported Upstream version 1.1.91 upstream/1.1.91
Diffstat (limited to 'src/NetworkManagerUtils.c')
-rw-r--r--src/NetworkManagerUtils.c2989
1 files changed, 116 insertions, 2873 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index fb680d96..50fe4efd 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -15,1909 +15,25 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2004 - 2014 Red Hat, Inc.
+ * Copyright 2004 - 2016 Red Hat, Inc.
  * Copyright 2005 - 2008 Novell, Inc.
  */
 
-#include "config.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <resolv.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <linux/if.h>
-#include <linux/if_infiniband.h>
-
 #include "nm-default.h"
+
 #include "NetworkManagerUtils.h"
-#include "nm-platform.h"
+
 #include "nm-utils.h"
-#include "nm-core-internal.h"
-#include "nm-device.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-wireless-security.h"
-#include "nm-auth-utils.h"
-
-/*
- * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export
- * CLOCK_BOOTTIME even though the kernel supports it, so provide a
- * local definition
- */
-#ifndef CLOCK_BOOTTIME
-#define CLOCK_BOOTTIME 7
-#endif
-
-G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int));
-int _nm_utils_testing = 0;
-
-gboolean
-nm_utils_get_testing_initialized ()
-{
-	NMUtilsTestFlags flags;
-
-	flags = (NMUtilsTestFlags) _nm_utils_testing;
-	if (flags == NM_UTILS_TEST_NONE)
-		flags = (NMUtilsTestFlags) g_atomic_int_get (&_nm_utils_testing);
-	return flags != NM_UTILS_TEST_NONE;
-}
-
-NMUtilsTestFlags
-nm_utils_get_testing ()
-{
-	NMUtilsTestFlags flags;
-
-	flags = (NMUtilsTestFlags) _nm_utils_testing;
-	if (flags != NM_UTILS_TEST_NONE) {
-		/* Flags already initialized. Return them. */
-		return flags & NM_UTILS_TEST_ALL;
-	}
-
-	/* Accessing nm_utils_get_testing() causes us to set the flags to initialized.
-	 * Detecting running tests also based on g_test_initialized(). */
-	flags = _NM_UTILS_TEST_INITIALIZED;
-	if (g_test_initialized ())
-		flags |= _NM_UTILS_TEST_GENERAL;
-
-	if (g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) {
-		/* Done. We set it. */
-		return flags & NM_UTILS_TEST_ALL;
-	}
-	/* It changed in the meantime (??). Re-read the value. */
-	return ((NMUtilsTestFlags) _nm_utils_testing) & NM_UTILS_TEST_ALL;
-}
-
-void
-_nm_utils_set_testing (NMUtilsTestFlags flags)
-{
-	g_assert (!NM_FLAGS_ANY (flags, ~NM_UTILS_TEST_ALL));
-
-	/* mask out everything except ALL, and always set GENERAL. */
-	flags = (flags & NM_UTILS_TEST_ALL) | (_NM_UTILS_TEST_GENERAL | _NM_UTILS_TEST_INITIALIZED);
-
-	if (!g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) {
-		/* We only allow setting _nm_utils_set_testing() once, before fetching the
-		 * value with nm_utils_get_testing(). */
-		g_return_if_reached ();
-	}
-}
-
-/*****************************************************************************/
-
-G_DEFINE_QUARK (nm-utils-error-quark, nm_utils_error)
-
-void
-nm_utils_error_set_cancelled (GError **error,
-                              gboolean is_disposing,
-                              const char *instance_name)
-{
-	if (is_disposing) {
-		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING,
-		             "Disposing %s instance",
-		             instance_name && *instance_name ? instance_name : "source");
-	} else {
-		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
-		                     "Request cancelled");
-	}
-}
-
-gboolean
-nm_utils_error_is_cancelled (GError *error,
-                             gboolean consider_is_disposing)
-{
-	if (error) {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			return TRUE;
-		if (   consider_is_disposing
-		    && g_error_matches (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING))
-			return TRUE;
-	}
-	return FALSE;
-}
-
-/*****************************************************************************/
-
-static GSList *_singletons = NULL;
-static gboolean _singletons_shutdown = FALSE;
-
-static void
-_nm_singleton_instance_weak_cb (gpointer data,
-                                GObject *where_the_object_was)
-{
-	_singletons = g_slist_remove (_singletons, where_the_object_was);
-}
-
-static void __attribute__((destructor))
-_nm_singleton_instance_destroy (void)
-{
-	_singletons_shutdown = TRUE;
-
-	while (_singletons) {
-		GObject *instance = _singletons->data;
-
-		_singletons = g_slist_delete_link (_singletons, _singletons);
-
-		g_object_weak_unref (instance, _nm_singleton_instance_weak_cb, NULL);
-
-		if (instance->ref_count > 1)
-			nm_log_dbg (LOGD_CORE, "disown %s singleton (%p)", G_OBJECT_TYPE_NAME (instance), instance);
-
-		g_object_unref (instance);
-	}
-}
-
-void
-_nm_singleton_instance_register_destruction (GObject *instance)
-{
-	g_return_if_fail (G_IS_OBJECT (instance));
-
-	/* Don't allow registration after shutdown. We only destroy the singletons
-	 * once. */
-	g_return_if_fail (!_singletons_shutdown);
-
-	g_object_weak_ref (instance, _nm_singleton_instance_weak_cb, NULL);
-
-	_singletons = g_slist_prepend (_singletons, instance);
-}
-
-/*****************************************************************************/
-
-gint
-nm_utils_ascii_str_to_bool (const char *str,
-                            gint default_value)
-{
-	gsize len;
-	char *s = NULL;
-
-	if (!str)
-		return default_value;
-
-	while (str[0] && g_ascii_isspace (str[0]))
-		str++;
-
-	if (!str[0])
-		return default_value;
-
-	len = strlen (str);
-	if (g_ascii_isspace (str[len - 1])) {
-		s = g_strdup (str);
-		g_strchomp (s);
-		str = s;
-	}
-
-	if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
-		default_value = TRUE;
-	else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
-		default_value = FALSE;
-	if (s)
-		g_free (s);
-	return default_value;
-}
-
-/*****************************************************************************/
-
-/*
- * nm_ethernet_address_is_valid:
- * @addr: pointer to a binary or ASCII Ethernet address
- * @len: length of @addr, or -1 if @addr is ASCII
- *
- * Compares an Ethernet address against known invalid addresses.
-
- * Returns: %TRUE if @addr is a valid Ethernet address, %FALSE if it is not.
- */
-gboolean
-nm_ethernet_address_is_valid (gconstpointer addr, gssize len)
-{
-	guint8 invalid_addr[4][ETH_ALEN] = {
-	    {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
-	    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-	    {0x44, 0x44, 0x44, 0x44, 0x44, 0x44},
-	    {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}, /* prism54 dummy MAC */
-	};
-	guint8 addr_bin[ETH_ALEN];
-	guint i;
-
-	if (!addr) {
-		g_return_val_if_fail (len == -1 || len == ETH_ALEN, FALSE);
-		return FALSE;
-	}
-
-	if (len == -1) {
-		if (!nm_utils_hwaddr_aton (addr, addr_bin, ETH_ALEN))
-			return FALSE;
-		addr = addr_bin;
-	} else if (len != ETH_ALEN)
-		g_return_val_if_reached (FALSE);
-
-	/* Check for multicast address */
-	if ((((guint8 *) addr)[0]) & 0x01)
-		return FALSE;
-
-	for (i = 0; i < G_N_ELEMENTS (invalid_addr); i++) {
-		if (nm_utils_hwaddr_matches (addr, ETH_ALEN, invalid_addr[i], ETH_ALEN))
-			return FALSE;
-	}
-
-	return TRUE;
-}
-
-
-/* nm_utils_ip4_address_clear_host_address:
- * @addr: source ip6 address
- * @plen: prefix length of network
- *
- * returns: the input address, with the host address set to 0.
- */
-in_addr_t
-nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen)
-{
-	return addr & nm_utils_ip4_prefix_to_netmask (plen);
-}
-
-/* nm_utils_ip6_address_clear_host_address:
- * @dst: destination output buffer, will contain the network part of the @src address
- * @src: source ip6 address
- * @plen: prefix length of network
- *
- * Note: this function is self assignment safe, to update @src inplace, set both
- * @dst and @src to the same destination.
- */
-const struct in6_addr *
-nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen)
-{
-	g_return_val_if_fail (plen <= 128, NULL);
-	g_return_val_if_fail (src, NULL);
-	g_return_val_if_fail (dst, NULL);
-
-	if (plen < 128) {
-		guint nbytes = plen / 8;
-		guint nbits = plen % 8;
-
-		if (nbytes && dst != src)
-			memcpy (dst, src, nbytes);
-		if (nbits) {
-			dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
-			nbytes++;
-		}
-		if (nbytes <= 15)
-			memset (&dst->s6_addr[nbytes], 0, 16 - nbytes);
-	} else if (src != dst)
-		*dst = *src;
-
-	return dst;
-}
-
-void
-nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len)
-{
-	gsize elt_size;
-	guint index_to_delete;
-	guint i_src;
-	guint mm_src, mm_dst, mm_len;
-	gsize i_itd;
-	guint res_length;
-
-	g_return_if_fail (array);
-	if (!len)
-		return;
-	g_return_if_fail (indexes_to_delete);
-
-	elt_size = g_array_get_element_size (array);
-
-	i_itd = 0;
-	index_to_delete = indexes_to_delete[0];
-	if (index_to_delete >= array->len)
-		g_return_if_reached ();
-
-	res_length = array->len - 1;
-
-	mm_dst = index_to_delete;
-	mm_src = index_to_delete;
-	mm_len = 0;
-
-	for (i_src = index_to_delete; i_src < array->len; i_src++) {
-		if (i_src < index_to_delete)
-			mm_len++;
-		else {
-			/* we require indexes_to_delete to contain non-repeated, ascending
-			 * indexes. Otherwise we would need to presort the indexes. */
-			while (TRUE) {
-				guint dd;
-
-				if (i_itd + 1 >= len) {
-					index_to_delete = G_MAXUINT;
-					break;
-				}
-
-				dd = indexes_to_delete[++i_itd];
-				if (dd > index_to_delete) {
-					if (dd >= array->len)
-						g_warn_if_reached ();
-					else {
-						g_assert (res_length > 0);
-						res_length--;
-					}
-					index_to_delete = dd;
-					break;
-				}
-				g_warn_if_reached ();
-			}
-
-			if (mm_len) {
-				memmove (&array->data[mm_dst * elt_size],
-				         &array->data[mm_src * elt_size],
-				         mm_len * elt_size);
-				mm_dst += mm_len;
-				mm_src += mm_len + 1;
-				mm_len = 0;
-			} else
-				mm_src++;
-		}
-	}
-	if (mm_len) {
-		memmove (&array->data[mm_dst * elt_size],
-		         &array->data[mm_src * elt_size],
-		         mm_len * elt_size);
-	}
-	g_array_set_size (array, res_length);
-}
-
-int
-nm_spawn_process (const char *args, GError **error)
-{
-	GError *local = NULL;
-	gint num_args;
-	char **argv = NULL;
-	int status = -1;
-
-	g_return_val_if_fail (args != NULL, -1);
-	g_return_val_if_fail (!error || !*error, -1);
-
-	if (g_shell_parse_argv (args, &num_args, &argv, &local)) {
-		g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local);
-		g_strfreev (argv);
-	}
-
-	if (local) {
-		nm_log_warn (LOGD_CORE, "could not spawn process '%s': %s", args, local->message);
-		g_propagate_error (error, local);
-	}
-
-	return status;
-}
-
-static const char *
-_trunk_first_line (char *str)
-{
-	char *s;
-
-	s = strchr (str, '\n');
-	if (s)
-		s[0] = '\0';
-	return str;
-}
-
-int
-nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *arg1, ...)
-{
-	gs_unref_ptrarray GPtrArray *argv = NULL;
-	int exit_status;
-	gs_free char *_log_str = NULL;
-#define ARGV_TO_STR(argv)   (_log_str ? _log_str : (_log_str = g_strjoinv (" ", (char **) argv->pdata)))
-	GError *local = NULL;
-	va_list ap;
-	NMLogLevel llevel = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR;
-	gs_free char *std_out = NULL, *std_err = NULL;
-
-	g_return_val_if_fail (!error || !*error, -1);
-	g_return_val_if_fail (arg1, -1);
-
-	/* construct the argument list */
-	argv = g_ptr_array_sized_new (4);
-	g_ptr_array_add (argv, "/sbin/modprobe");
-	g_ptr_array_add (argv, (char *) arg1);
-
-	va_start (ap, arg1);
-	while ((arg1 = va_arg (ap, const char *)))
-		g_ptr_array_add (argv, (char *) arg1);
-	va_end (ap);
-
-	g_ptr_array_add (argv, NULL);
-
-	nm_log_dbg (LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR (argv));
-	if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, &std_out, &std_err, &exit_status, &local)) {
-		nm_log (llevel, LOGD_CORE, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message);
-		g_propagate_error (error, local);
-		return -1;
-	} else if (exit_status != 0)
-		nm_log (llevel, LOGD_CORE, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status,
-		        std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "",
-		        std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : "");
-
-	return exit_status;
-}
-
-/**
- * nm_utils_get_start_time_for_pid:
- * @pid: the process identifier
- * @out_state: return the state character, like R, S, Z. See `man 5 proc`.
- * @out_ppid: parent process id
- *
- * Originally copied from polkit source (src/polkit/polkitunixprocess.c)
- * and adjusted.
- *
- * Returns: the timestamp when the process started (by parsing /proc/$PID/stat).
- * If an error occurs (e.g. the process does not exist), 0 is returned.
- *
- * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds)
- **/
-guint64
-nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
-{
-	guint64 start_time;
-	gs_free gchar *filename = NULL;
-	gs_free gchar *contents = NULL;
-	size_t length;
-	gs_strfreev gchar **tokens = NULL;
-	guint num_tokens;
-	gchar *p;
-	gchar *endp;
-	char state = '\0';
-	gint64 ppid = 0;
-
-	start_time = 0;
-	contents = NULL;
-
-	g_return_val_if_fail (pid > 0, 0);
-
-	filename = g_strdup_printf ("/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
-
-	if (!g_file_get_contents (filename, &contents, &length, NULL))
-		goto out;
-
-	/* start time is the token at index 19 after the '(process name)' entry - since only this
-	 * field can contain the ')' character, search backwards for this to avoid malicious
-	 * processes trying to fool us
-	 */
-	p = strrchr (contents, ')');
-	if (p == NULL)
-		goto out;
-	p += 2; /* skip ') ' */
-	if (p - contents >= (int) length)
-		goto out;
-
-	state = p[0];
-
-	tokens = g_strsplit (p, " ", 0);
-
-	num_tokens = g_strv_length (tokens);
-
-	if (num_tokens < 20)
-		goto out;
-
-	if (out_ppid)
-		ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
-
-	errno = 0;
-	start_time = strtoull (tokens[19], &endp, 10);
-	if (*endp != '\0' || errno != 0)
-		start_time = 0;
-
-out:
-	if (out_state)
-		*out_state = state;
-	if (out_ppid)
-		*out_ppid = ppid;
-
-	return start_time;
-}
-
-/******************************************************************************************/
-
-typedef struct {
-	pid_t pid;
-	NMLogDomain log_domain;
-	union {
-		struct {
-			gint64 wait_start_us;
-			guint source_timeout_kill_id;
-		} async;
-		struct {
-			gboolean success;
-			int child_status;
-		} sync;
-	};
-	NMUtilsKillChildAsyncCb callback;
-	void *user_data;
-
-	char log_name[1]; /* variable-length object, must be last element!! */
-} KillChildAsyncData;
-
-#define LOG_NAME_FMT "kill child process '%s' (%ld)"
-#define LOG_NAME_PROCESS_FMT "kill process '%s' (%ld)"
-#define LOG_NAME_ARGS log_name,(long)pid
-
-static KillChildAsyncData *
-_kc_async_data_alloc (pid_t pid, NMLogDomain log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data)
-{
-	KillChildAsyncData *data;
-	size_t log_name_len;
-
-	/* append the name at the end of our KillChildAsyncData. */
-	log_name_len = strlen (LOG_NAME_FMT) + 20 + strlen (log_name);
-	data = g_malloc (sizeof (KillChildAsyncData) - 1 + log_name_len);
-	g_snprintf (data->log_name, log_name_len, LOG_NAME_FMT, LOG_NAME_ARGS);
-
-	data->pid = pid;
-	data->user_data = user_data;
-	data->callback = callback;
-	data->log_domain = log_domain;
-
-	return data;
-}
-
-#define KC_EXIT_TO_STRING_BUF_SIZE 128
-static const char *
-_kc_exit_to_string (char *buf, int exit)
-#define _kc_exit_to_string(buf, exit) ( G_STATIC_ASSERT_EXPR(sizeof (buf) == KC_EXIT_TO_STRING_BUF_SIZE && sizeof ((buf)[0]) == 1), _kc_exit_to_string (buf, exit) )
-{
-	if (WIFEXITED (exit))
-		g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "normally with status %d", WEXITSTATUS (exit));
-	else if (WIFSIGNALED (exit))
-		g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "by signal %d", WTERMSIG (exit));
-	else
-		g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "with unexpected status %d", exit);
-	return buf;
-}
-
-static const char *
-_kc_signal_to_string (int sig)
-{
-	switch (sig) {
-	case 0:  return "no signal (0)";
-	case SIGKILL:  return "SIGKILL (" G_STRINGIFY (SIGKILL) ")";
-	case SIGTERM:  return "SIGTERM (" G_STRINGIFY (SIGTERM) ")";
-	default:
-		return "Unexpected signal";
-	}
-}
-
-#define KC_WAITED_TO_STRING 100
-static const char *
-_kc_waited_to_string (char *buf, gint64 wait_start_us)
-#define _kc_waited_to_string(buf, wait_start_us) ( G_STATIC_ASSERT_EXPR(sizeof (buf) == KC_WAITED_TO_STRING && sizeof ((buf)[0]) == 1), _kc_waited_to_string (buf, wait_start_us) )
-{
-	g_snprintf (buf, KC_WAITED_TO_STRING, " (%ld usec elapsed)", (long) (nm_utils_get_monotonic_timestamp_us () - wait_start_us));
-	return buf;
-}
-
-static void
-_kc_cb_watch_child (GPid pid, gint status, gpointer user_data)
-{
-	KillChildAsyncData *data = user_data;
-	char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE], buf_wait[KC_WAITED_TO_STRING];
-
-	if (data->async.source_timeout_kill_id)
-		g_source_remove (data->async.source_timeout_kill_id);
-
-	nm_log_dbg (data->log_domain, "%s: terminated %s%s",
-	            data->log_name, _kc_exit_to_string (buf_exit, status),
-	            _kc_waited_to_string (buf_wait, data->async.wait_start_us));
-
-	if (data->callback)
-		data->callback (pid, TRUE, status, data->user_data);
-
-	g_free (data);
-}
-
-static gboolean
-_kc_cb_timeout_grace_period (void *user_data)
-{
-	KillChildAsyncData *data = user_data;
-	int ret, errsv;
-
-	data->async.source_timeout_kill_id = 0;
-
-	if ((ret = kill (data->pid, SIGKILL)) != 0) {
-		errsv = errno;
-		/* ESRCH means, process does not exist or is already a zombie. */
-		if (errsv != ESRCH) {
-			nm_log_err (LOGD_CORE | data->log_domain, "%s: kill(SIGKILL) returned unexpected return value %d: (%s, %d)",
-			            data->log_name, ret, strerror (errsv), errsv);
-		}
-	} else {
-		nm_log_dbg (data->log_domain, "%s: process not terminated after %ld usec. Sending SIGKILL signal",
-		            data->log_name, (long) (nm_utils_get_monotonic_timestamp_us () - data->async.wait_start_us));
-	}
-
-	return G_SOURCE_REMOVE;
-}
-
-static gboolean
-_kc_invoke_callback_idle (gpointer user_data)
-{
-	KillChildAsyncData *data = user_data;
-
-	if (data->sync.success) {
-		char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
-
-		nm_log_dbg (data->log_domain, "%s: invoke callback: terminated %s",
-		            data->log_name, _kc_exit_to_string (buf_exit, data->sync.child_status));
-	} else
-		nm_log_dbg (data->log_domain, "%s: invoke callback: killing child failed", data->log_name);
-
-	data->callback (data->pid, data->sync.success, data->sync.child_status, data->user_data);
-	g_free (data);
-
-	return G_SOURCE_REMOVE;
-}
-
-static void
-_kc_invoke_callback (pid_t pid, NMLogDomain log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data, gboolean success, int child_status)
-{
-	KillChildAsyncData *data;
-
-	if (!callback)
-		return;
-
-	data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data);
-	data->sync.success = success;
-	data->sync.child_status = child_status;
-
-	g_idle_add (_kc_invoke_callback_idle, data);
-}
-
-/* nm_utils_kill_child_async:
- * @pid: the process id of the process to kill
- * @sig: signal to send initially. Set to 0 to send not signal.
- * @log_domain: the logging domain used for logging (LOGD_NONE to suppress logging)
- * @log_name: for logging, the name of the processes to kill
- * @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
- * to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter is ignored.
- * @callback: (allow-none): callback after the child terminated. This function will always
- *   be invoked asynchronously.
- * @user_data: passed on to callback
- *
- * Uses g_child_watch_add(), so note the glib comment: if you obtain pid from g_spawn_async() or
- * g_spawn_async_with_pipes() you will need to pass %G_SPAWN_DO_NOT_REAP_CHILD as flag to the spawn
- * function for the child watching to work.
- * Also note, that you must g_source_remove() any other child watchers for @pid because glib
- * supports only one watcher per child.
- **/
-void
-nm_utils_kill_child_async (pid_t pid, int sig, NMLogDomain log_domain,
-                           const char *log_name, guint32 wait_before_kill_msec,
-                           NMUtilsKillChildAsyncCb callback, void *user_data)
-{
-	int status = 0, errsv;
-	pid_t ret;
-	KillChildAsyncData *data;
-	char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
-
-	g_return_if_fail (pid > 0);
-	g_return_if_fail (log_name != NULL);
-
-	/* let's see if the child already terminated... */
-	ret = waitpid (pid, &status, WNOHANG);
-	if (ret > 0) {
-		nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
-		            LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
-		_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status);
-		return;
-	} else if (ret != 0) {
-		errsv = errno;
-		/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
-		if (errsv != ECHILD) {
-			nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)",
-			            LOG_NAME_ARGS, strerror (errsv), errsv);
-			_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
-			return;
-		}
-	}
-
-	/* send the first signal. */
-	if (kill (pid, sig) != 0) {
-		errsv = errno;
-		/* ESRCH means, process does not exist or is already a zombie. */
-		if (errsv != ESRCH) {
-			nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error sending %s: %s (%d)",
-			            LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
-			_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
-			return;
-		}
-
-		/* let's try again with waitpid, probably there was a race... */
-		ret = waitpid (pid, &status, 0);
-		if (ret > 0) {
-			nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
-			            LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
-			_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status);
-		} else {
-			errsv = errno;
-			nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s",
-			            LOG_NAME_ARGS, (long) ret, strerror (errsv), errsv, _kc_signal_to_string (sig));
-			_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
-		}
-		return;
-	}
-
-	data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data);
-	data->async.wait_start_us = nm_utils_get_monotonic_timestamp_us ();
-
-	if (sig != SIGKILL && wait_before_kill_msec > 0) {
-		data->async.source_timeout_kill_id = g_timeout_add (wait_before_kill_msec, _kc_cb_timeout_grace_period, data);
-		nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s (send SIGKILL in %ld milliseconds)...",
-		            data->log_name,  _kc_signal_to_string (sig), (long) wait_before_kill_msec);
-	} else {
-		data->async.source_timeout_kill_id = 0;
-		nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s...",
-		            data->log_name, _kc_signal_to_string (sig));
-	}
-
-	g_child_watch_add (pid, _kc_cb_watch_child, data);
-}
-
-static inline gulong
-_sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec)
-{
-	if (sleep_duration_msec > 0) {
-		guint64 x = (gint64) sleep_duration_msec * (guint64) 1000L;
-
-		return x < G_MAXULONG ? (gulong) x : G_MAXULONG;
-	}
-	return G_USEC_PER_SEC / 20;
-}
-
-/* nm_utils_kill_child_sync:
- * @pid: process id to kill
- * @sig: signal to sent initially. If 0, no signal is sent. If %SIGKILL, the
- * second %SIGKILL signal is not sent after @wait_before_kill_msec milliseconds.
- * @log_domain: log debug information for this domain. Errors and warnings are logged both
- * as %LOGD_CORE and @log_domain.
- * @log_name: name of the process to kill for logging.
- * @child_status: (out) (allow-none): return the exit status of the child, if no error occured.
- * @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
- * to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has not effect.
- * @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate.
- * Set to zero, to use the default (meaning 20 wakeups per seconds).
- *
- * Kill a child process synchronously and wait. The function first checks if the child already terminated
- * and if it did, return the exit status. Otherwise send one @sig signal. @sig  will always be
- * sent unless the child already exited. If the child does not exit within @wait_before_kill_msec milliseconds,
- * the function will send %SIGKILL and waits for the child indefinitly. If @wait_before_kill_msec is zero, no
- * %SIGKILL signal will be sent.
- *
- * In case of error, errno is preserved to contain the last reason of failure.
- **/
-gboolean
-nm_utils_kill_child_sync (pid_t pid, int sig, NMLogDomain log_domain, const char *log_name,
-                          int *child_status, guint32 wait_before_kill_msec,
-                          guint32 sleep_duration_msec)
-{
-	int status = 0, errsv = 0;
-	pid_t ret;
-	gboolean success = FALSE;
-	gboolean was_waiting = FALSE, send_kill = FALSE;
-	char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
-	char buf_wait[KC_WAITED_TO_STRING];
-	gint64 wait_start_us;
-
-	g_return_val_if_fail (pid > 0, FALSE);
-	g_return_val_if_fail (log_name != NULL, FALSE);
-
-	/* check if the child process already terminated... */
-	ret = waitpid (pid, &status, WNOHANG);
-	if (ret > 0) {
-		nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
-		            LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
-		success = TRUE;
-		goto out;
-	} else if (ret != 0) {
-		errsv = errno;
-		/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
-		if (errsv != ECHILD) {
-			nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)",
-			            LOG_NAME_ARGS, strerror (errsv), errsv);
-			goto out;
-		}
-	}
-
-	/* send first signal @sig */
-	if (kill (pid, sig) != 0) {
-		errsv = errno;
-		/* ESRCH means, process does not exist or is already a zombie. */
-		if (errsv != ESRCH) {
-			nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send %s: %s (%d)",
-			            LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
-		} else {
-			/* let's try again with waitpid, probably there was a race... */
-			ret = waitpid (pid, &status, 0);
-			if (ret > 0) {
-				nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
-				            LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
-				success = TRUE;
-			} else {
-				errsv = errno;
-				nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s",
-				            LOG_NAME_ARGS, (long) ret, strerror (errsv), errsv, _kc_signal_to_string (sig));
-			}
-		}
-		goto out;
-	}
-
-	wait_start_us = nm_utils_get_monotonic_timestamp_us ();
-
-	/* wait for the process to terminated... */
-	if (sig != SIGKILL) {
-		gint64 wait_until, now;
-		gulong sleep_time, sleep_duration_usec;
-		int loop_count = 0;
-
-		sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec);
-		wait_until = wait_before_kill_msec <= 0 ? 0 : wait_start_us + (((gint64) wait_before_kill_msec) * 1000L);
-
-		while (TRUE) {
-			ret = waitpid (pid, &status, WNOHANG);
-			if (ret > 0) {
-				nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s, process %ld exited %s%s",
-				            LOG_NAME_ARGS, _kc_signal_to_string (sig), (long) ret, _kc_exit_to_string (buf_exit, status),
-				            was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-				success = TRUE;
-				goto out;
-			}
-			if (ret == -1) {
-				errsv = errno;
-				/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
-				if (errsv != ECHILD) {
-					nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s, waitpid failed with %s (%d)%s",
-					            LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv,
-					           was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-					goto out;
-				}
-			}
-
-			if (!wait_until)
-				break;
-
-			now = nm_utils_get_monotonic_timestamp_us ();
-			if (now >= wait_until)
-				break;
-
-			if (!was_waiting) {
-				nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting up to %ld milliseconds for process to terminate normally after sending %s...",
-				            LOG_NAME_ARGS, (long) MAX (wait_before_kill_msec, 0), _kc_signal_to_string (sig));
-				was_waiting = TRUE;
-			}
-
-			sleep_time = MIN (wait_until - now, sleep_duration_usec);
-			if (loop_count < 20) {
-				/* At the beginning we expect the process to die fast.
-				 * Limit the sleep time, the limit doubles with every iteration. */
-				sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000);
-				loop_count++;
-			}
-			g_usleep (sleep_time);
-		}
-
-		/* send SIGKILL, if called with @wait_before_kill_msec > 0 */
-		if (wait_until) {
-			nm_log_dbg (log_domain, LOG_NAME_FMT ": sending SIGKILL...", LOG_NAME_ARGS);
-
-			send_kill = TRUE;
-			if (kill (pid, SIGKILL) != 0) {
-				errsv = errno;
-				/* ESRCH means, process does not exist or is already a zombie. */
-				if (errsv != ESRCH) {
-					nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send SIGKILL (after sending %s), %s (%d)",
-								LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
-					goto out;
-				}
-			}
-		}
-	}
-
-	if (!was_waiting) {
-		nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting for process to terminate after sending %s%s...",
-		            LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "");
-	}
-
-	/* block until the child terminates. */
-	while ((ret = waitpid (pid, &status, 0)) <= 0) {
-		errsv = errno;
-
-		if (errsv != EINTR) {
-			nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s%s, waitpid failed with %s (%d)%s",
-			            LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", strerror (errsv), errsv,
-			            _kc_waited_to_string (buf_wait, wait_start_us));
-			goto out;
-		}
-	}
-
-	nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s%s, process %ld exited %s%s",
-	            LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", (long) ret,
-	            _kc_exit_to_string (buf_exit, status), _kc_waited_to_string (buf_wait, wait_start_us));
-	success = TRUE;
-out:
-	if (child_status)
-		*child_status = success ? status : -1;
-	errno = success ? 0 : errsv;
-	return success;
-}
-
-/* nm_utils_kill_process_sync:
- * @pid: process id to kill
- * @start_time: the start time of the process to kill (as obtained by nm_utils_get_start_time_for_pid()).
- *   This is an optional argument, to avoid (somewhat) killing the wrong process as @pid
- *   might get recycled. You can pass 0, to not provide this parameter.
- * @sig: signal to sent initially. If 0, no signal is sent. If %SIGKILL, the
- *   second %SIGKILL signal is not sent after @wait_before_kill_msec milliseconds.
- * @log_domain: log debug information for this domain. Errors and warnings are logged both
- *   as %LOGD_CORE and @log_domain.
- * @log_name: name of the process to kill for logging.
- * @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
- *   to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has no effect.
- *   If @max_wait_msec is set but less then @wait_before_kill_msec, the final %SIGKILL will also
- *   not be send.
- * @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate.
- *   Set to zero, to use the default (meaning 20 wakeups per seconds).
- * @max_wait_msec: if 0, waits indefinitely until the process is gone (or a zombie). Otherwise, this
- *   is the maxium wait time until returning. If @max_wait_msec is non-zero but smaller then @wait_before_kill_msec,
- *   we will not send a final %SIGKILL.
- *
- * Kill a non-child process synchronously and wait. This function will not return before the
- * process with PID @pid is gone, the process is a zombie, or @max_wait_msec expires.
- **/
-void
-nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, NMLogDomain log_domain,
-                            const char *log_name, guint32 wait_before_kill_msec,
-                            guint32 sleep_duration_msec, guint32 max_wait_msec)
-{
-	int errsv;
-	guint64 start_time0;
-	gint64 wait_until_sigkill, now, wait_start_us, max_wait_until;
-	gulong sleep_time, sleep_duration_usec;
-	int loop_count = 0;
-	gboolean was_waiting = FALSE;
-	char buf_wait[KC_WAITED_TO_STRING];
-	char p_state;
-
-	g_return_if_fail (pid > 0);
-	g_return_if_fail (log_name != NULL);
-	g_return_if_fail (wait_before_kill_msec > 0);
-
-	start_time0 = nm_utils_get_start_time_for_pid (pid, &p_state, NULL);
-	if (start_time0 == 0) {
-		nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it seems already gone",
-		            LOG_NAME_ARGS, (long int) pid);
-		return;
-	}
-	if (start_time != 0 && start_time != start_time0) {
-		nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": don't kill process %ld because the start_time is unexpectedly %lu instead of %ld",
-		            LOG_NAME_ARGS, (long int) pid, (long unsigned) start_time0, (long unsigned) start_time);
-		return;
-	}
-
-	switch (p_state) {
-	case 'Z':
-	case 'x':
-	case 'X':
-		nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it is already a zombie (%c)",
-		            LOG_NAME_ARGS, (long int) pid, p_state);
-		return;
-	default:
-		break;
-	}
-
-	if (kill (pid, sig) != 0) {
-		errsv = errno;
-		/* ESRCH means, process does not exist or is already a zombie. */
-		if (errsv == ESRCH) {
-			nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": failed to send %s because process seems gone",
-			            LOG_NAME_ARGS, _kc_signal_to_string (sig));
-		} else {
-			nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to send %s: %s (%d)",
-			             LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
-		}
-		return;
-	}
-
-	/* wait for the process to terminated... */
-
-	wait_start_us = nm_utils_get_monotonic_timestamp_us ();
-
-	sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec);
-	if (sig != SIGKILL)
-		wait_until_sigkill = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L);
-	else
-		wait_until_sigkill = 0;
-	if (max_wait_msec > 0) {
-		max_wait_until = wait_start_us + (((gint64) max_wait_msec) * 1000L);
-		if (wait_until_sigkill > 0 && wait_until_sigkill > max_wait_msec)
-			wait_until_sigkill = 0;
-	} else
-		max_wait_until = 0;
-
-	while (TRUE) {
-		start_time = nm_utils_get_start_time_for_pid (pid, &p_state, NULL);
-
-		if (start_time != start_time0) {
-			nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone after sending signal %s%s",
-			            LOG_NAME_ARGS, _kc_signal_to_string (sig),
-			            was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-			return;
-		}
-		switch (p_state) {
-		case 'Z':
-		case 'x':
-		case 'X':
-			nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is a zombie (%c) after sending signal %s%s",
-			            LOG_NAME_ARGS, p_state, _kc_signal_to_string (sig),
-			            was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-			return;
-		default:
-			break;
-		}
-
-		if (kill (pid, 0) != 0) {
-			errsv = errno;
-			/* ESRCH means, process does not exist or is already a zombie. */
-			if (errsv == ESRCH) {
-				nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone or a zombie after sending signal %s%s",
-				            LOG_NAME_ARGS, _kc_signal_to_string (sig),
-				            was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-			} else {
-				nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to kill(%ld, 0): %s (%d)%s",
-				             LOG_NAME_ARGS, (long int) pid, strerror (errsv), errsv,
-				             was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-			}
-			return;
-		}
-
-		sleep_time = sleep_duration_usec;
-		now = nm_utils_get_monotonic_timestamp_us ();
-
-		if (   max_wait_until != 0
-		    && now >= max_wait_until) {
-			if (wait_until_sigkill != 0) {
-				/* wait_before_kill_msec is not larger then max_wait_until but we did not yet send
-				 * SIGKILL. Although we already reached our timeout, we don't want to skip sending
-				 * the signal. Even if we don't wait for the process to disappear. */
-				nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS);
-				kill (pid, SIGKILL);
-			}
-			nm_log_warn (log_domain, LOG_NAME_PROCESS_FMT ": timeout %u msec waiting for process to disappear (after sending %s)%s",
-			             LOG_NAME_ARGS, (unsigned) max_wait_until, _kc_signal_to_string (sig),
-			             was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
-			return;
-		}
-
-		if (wait_until_sigkill != 0) {
-			if (now >= wait_until_sigkill) {
-				/* Still not dead. SIGKILL now... */
-				nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS);
-				if (kill (pid, SIGKILL) != 0) {
-					errsv = errno;
-					/* ESRCH means, process does not exist or is already a zombie. */
-					if (errsv != ESRCH) {
-						nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone or a zombie%s",
-						            LOG_NAME_ARGS, _kc_waited_to_string (buf_wait, wait_start_us));
-					} else {
-						nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to send SIGKILL (after sending %s), %s (%d)%s",
-						             LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv,
-						             _kc_waited_to_string (buf_wait, wait_start_us));
-					}
-					return;
-				}
-				sig = SIGKILL;
-				wait_until_sigkill = 0;
-				loop_count = 0; /* reset the loop_count. Now we really expect the process to die quickly. */
-			} else
-				sleep_time = MIN (wait_until_sigkill - now, sleep_duration_usec);
-		}
-
-		if (!was_waiting) {
-			if (wait_until_sigkill != 0) {
-				nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear before sending KILL signal after sending %s...",
-				            LOG_NAME_ARGS, (long) wait_before_kill_msec, _kc_signal_to_string (sig));
-			} else if (max_wait_until != 0) {
-				nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear after sending %s...",
-				            LOG_NAME_ARGS, (long) max_wait_msec, _kc_signal_to_string (sig));
-			} else {
-				nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting for process to disappear after sending %s...",
-				            LOG_NAME_ARGS, _kc_signal_to_string (sig));
-			}
-			was_waiting = TRUE;
-		}
-
-		if (loop_count < 20) {
-			/* At the beginning we expect the process to die fast.
-			 * Limit the sleep time, the limit doubles with every iteration. */
-			sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000);
-			loop_count++;
-		}
-		g_usleep (sleep_time);
-	}
-}
-#undef LOG_NAME_FMT
-#undef LOG_NAME_PROCESS_FMT
-#undef LOG_NAME_ARGS
-
-const char *const NM_PATHS_DEFAULT[] = {
-	PREFIX "/sbin/",
-	PREFIX "/bin/",
-	"/sbin/",
-	"/usr/sbin/",
-	"/usr/local/sbin/",
-	"/bin/",
-	"/usr/bin/",
-	"/usr/local/bin/",
-	NULL,
-};
-
-const char *
-nm_utils_find_helper(const char *progname, const char *try_first, GError **error)
-{
-	return nm_utils_file_search_in_paths (progname, try_first, NM_PATHS_DEFAULT, G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error);
-}
-
-/******************************************************************************************/
-
-#define MAC_TAG "mac:"
-#define INTERFACE_NAME_TAG "interface-name:"
-#define DEVICE_TYPE_TAG "type:"
-#define SUBCHAN_TAG "s390-subchannels:"
-#define EXCEPT_TAG "except:"
-#define MATCH_TAG_CONFIG_NM_VERSION             "nm-version:"
-#define MATCH_TAG_CONFIG_NM_VERSION_MIN         "nm-version-min:"
-#define MATCH_TAG_CONFIG_NM_VERSION_MAX         "nm-version-max:"
-#define MATCH_TAG_CONFIG_ENV                    "env:"
-
-#define _spec_has_prefix(pspec, tag) \
-	({ \
-		const char **_spec = (pspec); \
-		gboolean _has = FALSE; \
-		\
-		if (!g_ascii_strncasecmp (*_spec, (""tag), STRLEN (tag))) { \
-			*_spec += STRLEN (tag); \
-			_has = TRUE; \
-		} \
-		_has; \
-	})
-
-static const char *
-_match_except (const char *spec_str, gboolean *out_except)
-{
-	if (!g_ascii_strncasecmp (spec_str, EXCEPT_TAG, STRLEN (EXCEPT_TAG))) {
-		spec_str += STRLEN (EXCEPT_TAG);
-		*out_except = TRUE;
-	} else
-		*out_except = FALSE;
-	return spec_str;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_device_type (const GSList *specs, const char *device_type)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	if (!device_type || !*device_type)
-		return NM_MATCH_SPEC_NO_MATCH;
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)) != 0)
-			continue;
-
-		spec_str += STRLEN (DEVICE_TYPE_TAG);
-		if (strcmp (spec_str, device_type) == 0) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	g_return_val_if_fail (hwaddr != NULL, NM_MATCH_SPEC_NO_MATCH);
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (   !g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))
-		    || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))
-		    || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)))
-			continue;
-
-		if (!g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG)))
-			spec_str += STRLEN (MAC_TAG);
-		else if (except)
-			continue;
-
-		if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1)) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_interface_name (const GSList *specs, const char *interface_name)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	g_return_val_if_fail (interface_name != NULL, NM_MATCH_SPEC_NO_MATCH);
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean use_pattern = FALSE;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (   !g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG))
-		    || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))
-		    || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)))
-			continue;
-
-		if (!g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))) {
-			spec_str += STRLEN (INTERFACE_NAME_TAG);
-			if (spec_str[0] == '=')
-				spec_str += 1;
-			else {
-				if (spec_str[0] == '~')
-					spec_str += 1;
-				use_pattern=TRUE;
-			}
-		} else if (except)
-			continue;
-
-		if (   !strcmp (spec_str, interface_name)
-		    || (use_pattern && g_pattern_match_simple (spec_str, interface_name))) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-#define BUFSIZE 10
-
-static gboolean
-parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
-{
-	long unsigned int tmp;
-	char buf[BUFSIZE + 1];
-	const char *p = subchannels;
-	int i = 0;
-	char *pa = NULL, *pb = NULL, *pc = NULL;
-
-	g_return_val_if_fail (subchannels != NULL, FALSE);
-	g_return_val_if_fail (a != NULL, FALSE);
-	g_return_val_if_fail (*a == 0, FALSE);
-	g_return_val_if_fail (b != NULL, FALSE);
-	g_return_val_if_fail (*b == 0, FALSE);
-	g_return_val_if_fail (c != NULL, FALSE);
-	g_return_val_if_fail (*c == 0, FALSE);
-
-	/* sanity check */
-	if (!g_ascii_isxdigit (subchannels[0]))
-		return FALSE;
-
-	/* Get the first channel */
-	while (*p && (*p != ',')) {
-		if (!g_ascii_isxdigit (*p) && (*p != '.'))
-			return FALSE;  /* Invalid chars */
-		if (i >= BUFSIZE)
-			return FALSE;  /* Too long to be a subchannel */
-		buf[i++] = *p++;
-	}
-	buf[i] = '\0';
-
-	/* and grab each of its elements, there should be 3 */
-	pa = &buf[0];
-	pb = strchr (buf, '.');
-	if (pb)
-		pc = strchr (pb + 1, '.');
-	if (!pa || !pb || !pc)
-		return FALSE;
-
-	/* Split the string */
-	*pb++ = '\0';
-	*pc++ = '\0';
-
-	errno = 0;
-	tmp = strtoul (pa, NULL, 16);
-	if (errno)
-		return FALSE;
-	*a = (guint32) tmp;
-
-	errno = 0;
-	tmp = strtoul (pb, NULL, 16);
-	if (errno)
-		return FALSE;
-	*b = (guint32) tmp;
-
-	errno = 0;
-	tmp = strtoul (pc, NULL, 16);
-	if (errno)
-		return FALSE;
-	*c = (guint32) tmp;
-
-	return TRUE;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
-{
-	const GSList *iter;
-	guint32 a = 0, b = 0, c = 0;
-	guint32 spec_a = 0, spec_b = 0, spec_c = 0;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	g_return_val_if_fail (subchannels != NULL, NM_MATCH_SPEC_NO_MATCH);
-
-	if (!specs)
-		return NM_MATCH_SPEC_NO_MATCH;
-
-	if (!parse_subchannels (subchannels, &a, &b, &c))
-		return NM_MATCH_SPEC_NO_MATCH;
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (!g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))) {
-			spec_str += STRLEN (SUBCHAN_TAG);
-			if (parse_subchannels (spec_str, &spec_a, &spec_b, &spec_c)) {
-				if (a == spec_a && b == spec_b && c == spec_c) {
-					if (except)
-						return NM_MATCH_SPEC_NEG_MATCH;
-					match = NM_MATCH_SPEC_MATCH;
-				}
-			}
-		}
-	}
-	return match;
-}
-
-static gboolean
-_match_config_nm_version (const char *str, const char *tag, guint cur_nm_version)
-{
-	gs_free char *s_ver = NULL;
-	gs_strfreev char **s_ver_tokens = NULL;
-	gint v_maj = -1, v_min = -1, v_mic = -1;
-	guint c_maj = -1, c_min = -1, c_mic = -1;
-	guint n_tokens;
-
-	s_ver = g_strdup (str);
-	g_strstrip (s_ver);
-
-	/* Let's be strict with the accepted format here. No funny stuff!! */
-
-	if (s_ver[strspn (s_ver, ".0123456789")] != '\0')
-		return FALSE;
-
-	s_ver_tokens = g_strsplit (s_ver, ".", -1);
-	n_tokens = g_strv_length (s_ver_tokens);
-	if (n_tokens == 0 || n_tokens > 3)
-		return FALSE;
-
-	v_maj = _nm_utils_ascii_str_to_int64 (s_ver_tokens[0], 10, 0, 0xFFFF, -1);
-	if (v_maj < 0)
-		return FALSE;
-	if (n_tokens >= 2) {
-		v_min = _nm_utils_ascii_str_to_int64 (s_ver_tokens[1], 10, 0, 0xFF, -1);
-		if (v_min < 0)
-			return FALSE;
-	}
-	if (n_tokens >= 3) {
-		v_mic = _nm_utils_ascii_str_to_int64 (s_ver_tokens[2], 10, 0, 0xFF, -1);
-		if (v_mic < 0)
-			return FALSE;
-	}
-
-	nm_decode_version (cur_nm_version, &c_maj, &c_min, &c_mic);
-
-#define CHECK_AND_RETURN_FALSE(cur, val, tag, is_last_digit) \
-	G_STMT_START { \
-		if (!strcmp (tag, MATCH_TAG_CONFIG_NM_VERSION_MIN)) { \
-			if (cur < val) \
-				return FALSE; \
-		} else if (!strcmp (tag, MATCH_TAG_CONFIG_NM_VERSION_MAX)) { \
-			if (cur > val) \
-				return FALSE; \
-		} else { \
-			if (cur != val) \
-				return FALSE; \
-		} \
-		if (!(is_last_digit)) { \
-			if (cur != val) \
-				return FALSE; \
-		} \
-	} G_STMT_END
-	if (v_mic >= 0)
-		CHECK_AND_RETURN_FALSE (c_mic, v_mic, tag, TRUE);
-	if (v_min >= 0)
-		CHECK_AND_RETURN_FALSE (c_min, v_min, tag, v_mic < 0);
-	CHECK_AND_RETURN_FALSE (c_maj, v_maj, tag, v_min < 0);
-	return TRUE;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_match_config (const GSList *specs, guint cur_nm_version, const char *env)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	if (!specs)
-		return NM_MATCH_SPEC_NO_MATCH;
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
-		gboolean v_match;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_NM_VERSION))
-			v_match = _match_config_nm_version (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version);
-		else if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN))
-			v_match = _match_config_nm_version (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN, cur_nm_version);
-		else if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX))
-			v_match = _match_config_nm_version (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX, cur_nm_version);
-		else if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_ENV))
-			v_match = env && env[0] && !strcmp (spec_str, env);
-		else
-			continue;
-
-		if (v_match) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-/**
- * nm_match_spec_split:
- * @value: the string of device specs
- *
- * Splits the specs from the string and returns them as individual
- * entires in a #GSList.
- *
- * It does not validate any specs, it basically just does a special
- * strsplit with ',' or ';' as separators and supporting '\\' as
- * escape character.
- *
- * Leading and trailing spaces of each entry are removed. But the user
- * can preserve them by specifying "\\s has 2 leading" or "has 2 trailing \\s".
- *
- * Specs can have a qualifier like "interface-name:". We still don't strip
- * any whitespace after the colon, so "interface-name: X" matches an interface
- * named " X".
- *
- * Returns: (transfer-full): the list of device specs.
- */
-GSList *
-nm_match_spec_split (const char *value)
-{
-	char *string_value, *p, *q0, *q;
-	GSList *pieces = NULL;
-	int trailing_ws;
-
-	if (!value || !*value)
-		return NULL;
-
-	/* Copied from glibs g_key_file_parse_value_as_string() function
-	 * and adjusted. */
-
-	string_value = g_new (gchar, strlen (value) + 1);
-
-	p = (gchar *) value;
-
-	/* skip over leading whitespace */
-	while (g_ascii_isspace (*p))
-		p++;
-
-	q0 = q = string_value;
-	trailing_ws = 0;
-	while (*p) {
-		if (*p == '\\') {
-			p++;
-
-			switch (*p) {
-			case 's':
-				*q = ' ';
-				break;
-			case 'n':
-				*q = '\n';
-				break;
-			case 't':
-				*q = '\t';
-				break;
-			case 'r':
-				*q = '\r';
-				break;
-			case '\\':
-				*q = '\\';
-				break;
-			case '\0':
-				break;
-			default:
-				if (NM_IN_SET (*p, ',', ';'))
-					*q = *p;
-				else {
-					*q++ = '\\';
-					*q = *p;
-				}
-				break;
-			}
-			if (*p == '\0')
-				break;
-			p++;
-			trailing_ws = 0;
-		} else {
-			*q = *p;
-			if (*p == '\0')
-				break;
-			if (g_ascii_isspace (*p)) {
-				trailing_ws++;
-				p++;
-			} else if (NM_IN_SET (*p, ',', ';')) {
-				if (q0 < q - trailing_ws)
-					pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws));
-				q0 = q + 1;
-				p++;
-				trailing_ws = 0;
-				while (g_ascii_isspace (*p))
-					p++;
-			} else
-				p++;
-		}
-		q++;
-	}
-
-	*q = '\0';
-	if (q0 < q - trailing_ws)
-		pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws));
-	g_free (string_value);
-	return g_slist_reverse (pieces);
-}
-
-/**
- * nm_match_spec_join:
- * @specs: the device specs to join
- *
- * This is based on g_key_file_parse_string_as_value(), analog to
- * nm_match_spec_split() which is based on g_key_file_parse_value_as_string().
- *
- * Returns: (transfer-full): a joined list of device specs that can be
- *   split again with nm_match_spec_split(). Note that
- *   nm_match_spec_split (nm_match_spec_join (specs)) yields the original
- *   result (which is not true the other way around because there are multiple
- *   ways to encode the same joined specs string).
- */
-char *
-nm_match_spec_join (GSList *specs)
-{
-	const char *p;
-	GString *str;
-
-	str = g_string_new ("");
-
-	for (; specs; specs = specs->next) {
-		p = specs->data;
-
-		if (!p || !*p)
-			continue;
-
-		if (str->len > 0)
-			g_string_append_c (str, ',');
-
-		/* escape leading whitespace */
-		switch (*p) {
-		case ' ':
-			g_string_append (str, "\\s");
-			p++;
-			break;
-		case '\t':
-			g_string_append (str, "\\t");
-			p++;
-			break;
-		}
-
-		for (; *p; p++) {
-			switch (*p) {
-			case '\n':
-				g_string_append (str, "\\n");
-				break;
-			case '\r':
-				g_string_append (str, "\\r");
-				break;
-			case '\\':
-				g_string_append (str, "\\\\");
-				break;
-			case ',':
-				g_string_append (str, "\\,");
-				break;
-			case ';':
-				g_string_append (str, "\\;");
-				break;
-			default:
-				g_string_append_c (str, *p);
-				break;
-			}
-		}
-
-		/* escape trailing whitespaces */
-		switch (str->str[str->len - 1]) {
-		case ' ':
-			g_string_overwrite (str, str->len - 1, "\\s");
-			break;
-		case '\t':
-			g_string_overwrite (str, str->len - 1, "\\t");
-			break;
-		}
-	}
-
-	return g_string_free (str, FALSE);
-}
-
-/*****************************************************************************/
-
-char _nm_utils_to_string_buffer[];
-
-void
-nm_utils_to_string_buffer_init (char **buf, gsize *len)
-{
-	if (!*buf) {
-		*buf = _nm_utils_to_string_buffer;
-		*len = sizeof (_nm_utils_to_string_buffer);
-	}
-}
-
-gboolean
-nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len)
-{
-	nm_utils_to_string_buffer_init (buf, len);
-	if (!obj) {
-		g_strlcpy (*buf, "(null)", *len);
-		return FALSE;
-	}
-	return TRUE;
-}
-
-void
-nm_utils_strbuf_append_c (char **buf, gsize *len, char c)
-{
-	switch (*len) {
-	case 0:
-		return;
-	case 1:
-		(*buf)[0] = '\0';
-		*len = 0;
-		(*buf)++;
-		return;
-	default:
-		(*buf)[0] = c;
-		(*buf)[1] = '\0';
-		(*len)--;
-		(*buf)++;
-		return;
-	}
-}
-
-void
-nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str)
-{
-	gsize src_len;
-
-	switch (*len) {
-	case 0:
-		return;
-	case 1:
-		if (!str || !*str) {
-			(*buf)[0] = '\0';
-			return;
-		}
-		(*buf)[0] = '\0';
-		*len = 0;
-		(*buf)++;
-		return;
-	default:
-		if (!str || !*str) {
-			(*buf)[0] = '\0';
-			return;
-		}
-		src_len = g_strlcpy (*buf, str, *len);
-		if (src_len >= *len) {
-			*buf = &(*buf)[*len];
-			*len = 0;
-		} else {
-			*buf = &(*buf)[src_len];
-			*len -= src_len;
-		}
-		return;
-	}
-}
-
-void
-nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
-{
-	char *p = *buf;
-	va_list args;
-	gint retval;
-
-	if (*len == 0)
-		return;
-
-	va_start (args, format);
-	retval = g_vsnprintf (p, *len, format, args);
-	va_end (args);
-
-	if (retval >= *len) {
-		*buf = &p[*len];
-		*len = 0;
-	} else {
-		*buf = &p[retval];
-		*len -= retval;
-	}
-}
-
-const char *
-nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
-                    gsize n_descs,
-                    unsigned flags,
-                    char *buf,
-                    gsize len)
-{
-	gsize i;
-	char *p;
-
-#if NM_MORE_ASSERTS > 10
-	nm_assert (descs);
-	nm_assert (n_descs > 0);
-	for (i = 0; i < n_descs; i++) {
-		gsize j;
-
-		nm_assert (descs[i].flag && nm_utils_is_power_of_two (descs[i].flag));
-		nm_assert (descs[i].name && descs[i].name[0]);
-		for (j = 0; j < i; j++)
-			nm_assert (descs[j].flag != descs[i].flag);
-	}
-#endif
-
-	nm_utils_to_string_buffer_init (&buf, &len);
-
-	if (!len)
-		return buf;
-
-	buf[0] = '\0';
-	if (!flags) {
-		return buf;
-	}
-
-	p = buf;
-	for (i = 0; flags && i < n_descs; i++) {
-		if (NM_FLAGS_HAS (flags, descs[i].flag)) {
-			flags &= ~descs[i].flag;
-
-			if (buf[0] != '\0')
-				nm_utils_strbuf_append_c (&p, &len, ',');
-			nm_utils_strbuf_append_str (&p, &len, descs[i].name);
-		}
-	}
-	if (flags) {
-		if (buf[0] != '\0')
-			nm_utils_strbuf_append_c (&p, &len, ',');
-		nm_utils_strbuf_append (&p, &len, "0x%x", flags);
-	}
-	return buf;
-};
-
-/*****************************************************************************/
-
-const char *
-nm_utils_enum2str (const NMUtilsEnum2StrDesc *descs,
-                   gsize n_descs,
-                   int val,
-                   char *buf,
-                   gsize len)
-{
-	gsize i;
-
-#if NM_MORE_ASSERTS > 10
-	nm_assert (descs);
-	nm_assert (n_descs > 0);
-	for (i = 0; i < n_descs; i++) {
-		gsize j;
-
-		nm_assert (descs[i].name && descs[i].name[0]);
-		for (j = 0; j < i; j++)
-			nm_assert (descs[j].value != descs[i].value);
-	}
-#endif
-
-	nm_utils_to_string_buffer_init (&buf, &len);
-
-	if (!len)
-		return buf;
-
-	for (i = 0; i < n_descs; i++) {
-		if (val == descs[i].value) {
-			g_strlcpy (buf, descs[i].name, len);
-			return buf;
-		}
-	}
+#include "nm-core-internal.h"
 
-	g_snprintf (buf, len, "(%d)", val);
-	return buf;
-};
+#include "nm-platform.h"
+#include "nm-exported-object.h"
+#include "nm-auth-utils.h"
 
-/*****************************************************************************/
+/******************************************************************************/
 
 /**
  * nm_utils_get_shared_wifi_permission:
@@ -1948,7 +64,7 @@ nm_utils_get_shared_wifi_permission (NMConnection *connection)
 	return NULL;
 }
 
-/*********************************/
+/******************************************************************************/
 
 static char *
 get_new_connection_name (const GSList *existing,
@@ -2138,109 +254,7 @@ nm_utils_complete_generic (NMConnection *connection,
 	g_hash_table_destroy (parameters);
 }
 
-char *
-nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id)
-{
-	/* Basically VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD */
-	return g_strdup_printf ("%s.%d", parent_iface, vlan_id);
-}
-
-/**
- * nm_utils_read_resolv_conf_nameservers():
- * @rc_contents: contents of a resolv.conf; or %NULL to read /etc/resolv.conf
- *
- * Reads all nameservers out of @rc_contents or /etc/resolv.conf and returns
- * them.
- *
- * Returns: a #GPtrArray of 'char *' elements of each nameserver line from
- * @contents or resolv.conf
- */
-GPtrArray *
-nm_utils_read_resolv_conf_nameservers (const char *rc_contents)
-{
-	GPtrArray *nameservers = NULL;
-	char *contents = NULL;
-	char **lines, **iter;
-	char *p;
-
-	if (rc_contents)
-		contents = g_strdup (rc_contents);
-	else {
-		if (!g_file_get_contents (_PATH_RESCONF, &contents, NULL, NULL))
-			return NULL;
-	}
-
-	nameservers = g_ptr_array_new_full (3, g_free);
-
-	lines = g_strsplit_set (contents, "\r\n", -1);
-	for (iter = lines; *iter; iter++) {
-		if (!g_str_has_prefix (*iter, "nameserver"))
-			continue;
-		p = *iter + strlen ("nameserver");
-		if (!g_ascii_isspace (*p++))
-			continue;
-		/* Skip intermediate whitespace */
-		while (g_ascii_isspace (*p))
-			p++;
-		g_strchomp (p);
-
-		g_ptr_array_add (nameservers, g_strdup (p));
-	}
-	g_strfreev (lines);
-	g_free (contents);
-
-	return nameservers;
-}
-
-/**
- * nm_utils_read_resolv_conf_dns_options():
- * @rc_contents: contents of a resolv.conf; or %NULL to read /etc/resolv.conf
- *
- * Reads all dns options out of @rc_contents or /etc/resolv.conf and returns
- * them.
- *
- * Returns: a #GPtrArray of 'char *' elements of each option
- */
-GPtrArray *
-nm_utils_read_resolv_conf_dns_options (const char *rc_contents)
-{
-	GPtrArray *options = NULL;
-	char *contents = NULL;
-	char **lines, **line_iter;
-	char **tokens, **token_iter;
-	char *p;
-
-	if (rc_contents)
-		contents = g_strdup (rc_contents);
-	else {
-		if (!g_file_get_contents (_PATH_RESCONF, &contents, NULL, NULL))
-			return NULL;
-	}
-
-	options = g_ptr_array_new_full (3, g_free);
-
-	lines = g_strsplit_set (contents, "\r\n", -1);
-	for (line_iter = lines; *line_iter; line_iter++) {
-		if (!g_str_has_prefix (*line_iter, "options"))
-			continue;
-		p = *line_iter + strlen ("options");
-		if (!g_ascii_isspace (*p++))
-			continue;
-
-		tokens = g_strsplit (p, " ", 0);
-		for (token_iter = tokens; token_iter && *token_iter; token_iter++) {
-			g_strstrip (*token_iter);
-			if (!*token_iter[0])
-				continue;
-			g_ptr_array_add (options, g_strdup (*token_iter));
-		}
-		g_strfreev (tokens);
-	}
-	g_strfreev (lines);
-	g_free (contents);
-
-	return options;
-}
+/*****************************************************************************/
 
 static GHashTable *
 check_property_in_hash (GHashTable *hash,
@@ -2321,6 +335,95 @@ check_ip6_method (NMConnection *orig,
 	return allow;
 }
 
+static int
+route_compare (NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric)
+{
+	gint64 r, metric1, metric2;
+
+	r = g_strcmp0 (nm_ip_route_get_dest (route1), nm_ip_route_get_dest (route2));
+	if (r)
+		return r;
+
+	r = nm_ip_route_get_prefix (route1) - nm_ip_route_get_prefix (route2);
+	if (r)
+		return r > 0 ? 1 : -1;
+
+	r = g_strcmp0 (nm_ip_route_get_next_hop (route1), nm_ip_route_get_next_hop (route2));
+	if (r)
+		return r;
+
+	metric1 = nm_ip_route_get_metric (route1) == -1 ? default_metric : nm_ip_route_get_metric (route1);
+	metric2 = nm_ip_route_get_metric (route2) == -1 ? default_metric : nm_ip_route_get_metric (route2);
+
+	r = metric1 - metric2;
+	if (r)
+		return r > 0 ? 1 : -1;
+
+	r = nm_ip_route_get_family (route1) - nm_ip_route_get_family (route2);
+	if (r)
+		return r > 0 ? 1 : -1;
+
+	return 0;
+}
+
+static int
+route_ptr_compare (const void *a, const void *b)
+{
+	return route_compare (*(NMIPRoute **) a, *(NMIPRoute **) b, -1);
+}
+
+static gboolean
+check_ip_routes (NMConnection *orig,
+                 NMConnection *candidate,
+                 GHashTable *settings,
+                 gint64 default_metric,
+                 gboolean v4)
+{
+	gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL;
+	NMSettingIPConfig *s_ip1, *s_ip2;
+	const char *s_name;
+	GHashTable *props;
+	guint i, num;
+
+	s_name = v4 ? NM_SETTING_IP4_CONFIG_SETTING_NAME :
+	              NM_SETTING_IP6_CONFIG_SETTING_NAME;
+
+	props = check_property_in_hash (settings,
+	                                s_name,
+	                                NM_SETTING_IP_CONFIG_ROUTES);
+	if (!props)
+		return TRUE;
+
+	s_ip1 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (orig, s_name);
+	s_ip2 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (candidate, s_name);
+
+	if (!s_ip1 || !s_ip2)
+		return FALSE;
+
+	num = nm_setting_ip_config_get_num_routes (s_ip1);
+	if (num != nm_setting_ip_config_get_num_routes (s_ip2))
+		return FALSE;
+
+	routes1 = g_new (NMIPRoute *, num);
+	routes2 = g_new (NMIPRoute *, num);
+
+	for (i = 0; i < num; i++) {
+		routes1[i] = nm_setting_ip_config_get_route (s_ip1, i);
+		routes2[i] = nm_setting_ip_config_get_route (s_ip2, i);
+	}
+
+	qsort (routes1, num, sizeof (NMIPRoute *), route_ptr_compare);
+	qsort (routes2, num, sizeof (NMIPRoute *), route_ptr_compare);
+
+	for (i = 0; i < num; i++) {
+		if (route_compare (routes1[i], routes2[i], default_metric))
+			return FALSE;
+	}
+
+	remove_from_hash (settings, props, s_name, NM_SETTING_IP_CONFIG_ROUTES);
+	return TRUE;
+}
+
 static gboolean
 check_ip4_method (NMConnection *orig,
                   NMConnection *candidate,
@@ -2504,7 +607,9 @@ static NMConnection *
 check_possible_match (NMConnection *orig,
                       NMConnection *candidate,
                       GHashTable *settings,
-                      gboolean device_has_carrier)
+                      gboolean device_has_carrier,
+                      gint64 default_v4_metric,
+                      gint64 default_v6_metric)
 {
 	g_return_val_if_fail (settings != NULL, NULL);
 
@@ -2514,6 +619,12 @@ check_possible_match (NMConnection *orig,
 	if (!check_ip4_method (orig, candidate, settings, device_has_carrier))
 		return NULL;
 
+	if (!check_ip_routes (orig, candidate, settings, default_v4_metric, TRUE))
+		return NULL;
+
+	if (!check_ip_routes (orig, candidate, settings, default_v6_metric, FALSE))
+		return NULL;
+
 	if (!check_connection_interface_name (orig, candidate, settings))
 		return NULL;
 
@@ -2557,6 +668,8 @@ NMConnection *
 nm_utils_match_connection (GSList *connections,
                            NMConnection *original,
                            gboolean device_has_carrier,
+                           gint64 default_v4_metric,
+                           gint64 default_v6_metric,
                            NMUtilsMatchFilterFunc match_filter_func,
                            gpointer match_filter_data)
 {
@@ -2573,8 +686,10 @@ nm_utils_match_connection (GSList *connections,
 		}
 
 		if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
-			if (!best_match)
-				best_match = check_possible_match (original, candidate, diffs, device_has_carrier);
+			if (!best_match) {
+				best_match = check_possible_match (original, candidate, diffs, device_has_carrier,
+				                                   default_v4_metric, default_v6_metric);
+			}
 
 			if (!best_match && nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
 				GString *diff_string;
@@ -2614,860 +729,7 @@ nm_utils_match_connection (GSList *connections,
 	return best_match;
 }
 
-int
-nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection **b)
-{
-	NMSettingConnection *a_s_con, *b_s_con;
-	gboolean a_ac, b_ac;
-	gint a_ap, b_ap;
-
-	a_s_con = nm_connection_get_setting_connection (*a);
-	b_s_con = nm_connection_get_setting_connection (*b);
-
-	a_ac = !!nm_setting_connection_get_autoconnect (a_s_con);
-	b_ac = !!nm_setting_connection_get_autoconnect (b_s_con);
-	if (a_ac != b_ac)
-		return ((int) b_ac) - ((int) a_ac);
-	if (!a_ac)
-		return 0;
-
-	a_ap = nm_setting_connection_get_autoconnect_priority (a_s_con);
-	b_ap = nm_setting_connection_get_autoconnect_priority (b_s_con);
-	if (a_ap != b_ap)
-		return (a_ap > b_ap) ? -1 : 1;
-
-	return 0;
-}
-
-/**************************************************************************/
-
-static gint64 monotonic_timestamp_offset_sec;
-static int monotonic_timestamp_clock_mode = 0;
-
-static void
-monotonic_timestamp_get (struct timespec *tp)
-{
-	int clock_mode = 0;
-	int err = 0;
-
-	switch (monotonic_timestamp_clock_mode) {
-	case 0:
-		/* the clock is not yet initialized (first run) */
-		err = clock_gettime (CLOCK_BOOTTIME, tp);
-		if (err == -1 && errno == EINVAL) {
-			clock_mode = 2;
-			err = clock_gettime (CLOCK_MONOTONIC, tp);
-		} else
-			clock_mode = 1;
-		break;
-	case 1:
-		/* default, return CLOCK_BOOTTIME */
-		err = clock_gettime (CLOCK_BOOTTIME, tp);
-		break;
-	case 2:
-		/* fallback, return CLOCK_MONOTONIC. Kernels prior to 2.6.39
-		 * don't support CLOCK_BOOTTIME. */
-		err = clock_gettime (CLOCK_MONOTONIC, tp);
-		break;
-	}
-
-	g_assert (err == 0); (void)err;
-	g_assert (tp->tv_nsec >= 0 && tp->tv_nsec < NM_UTILS_NS_PER_SECOND);
-
-	if (G_LIKELY (clock_mode == 0))
-		return;
-
-	/* Calculate an offset for the time stamp.
-	 *
-	 * We always want positive values, because then we can initialize
-	 * a timestamp with 0 and be sure, that it will be less then any
-	 * value nm_utils_get_monotonic_timestamp_*() might return.
-	 * For this to be true also for nm_utils_get_monotonic_timestamp_s() at
-	 * early boot, we have to shift the timestamp to start counting at
-	 * least from 1 second onward.
-	 *
-	 * Another advantage of shifting is, that this way we make use of the whole 31 bit
-	 * range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_s()
-	 * wraps (~68 years).
-	 **/
-	monotonic_timestamp_offset_sec = (- ((gint64) tp->tv_sec)) + 1;
-	monotonic_timestamp_clock_mode = clock_mode;
-
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
-		time_t now = time (NULL);
-		struct tm tm;
-		char s[255];
-
-		strftime (s, sizeof (s), "%Y-%m-%d %H:%M:%S", localtime_r (&now, &tm));
-		nm_log_dbg (LOGD_CORE, "monotonic timestamp started counting 1.%09ld seconds ago with "
-		                       "an offset of %lld.0 seconds to %s (local time is %s)",
-		                       tp->tv_nsec, (long long) -monotonic_timestamp_offset_sec,
-		                       clock_mode == 1 ? "CLOCK_BOOTTIME" : "CLOCK_MONOTONIC", s);
-	}
-}
-
-/**
- * nm_utils_get_monotonic_timestamp_ns:
- *
- * Returns: a monotonically increasing time stamp in nanoseconds,
- * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME.
- *
- * The returned value will start counting at an undefined point
- * in the past and will always be positive.
- *
- * All the nm_utils_get_monotonic_timestamp_*s functions return the same
- * timestamp but in different scales (nsec, usec, msec, sec).
- **/
-gint64
-nm_utils_get_monotonic_timestamp_ns (void)
-{
-	struct timespec tp = { 0 };
-
-	monotonic_timestamp_get (&tp);
-
-	/* Although the result will always be positive, we return a signed
-	 * integer, which makes it easier to calculate time differences (when
-	 * you want to subtract signed values).
-	 **/
-	return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * NM_UTILS_NS_PER_SECOND +
-	       tp.tv_nsec;
-}
-
-/**
- * nm_utils_get_monotonic_timestamp_us:
- *
- * Returns: a monotonically increasing time stamp in microseconds,
- * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME.
- *
- * The returned value will start counting at an undefined point
- * in the past and will always be positive.
- *
- * All the nm_utils_get_monotonic_timestamp_*s functions return the same
- * timestamp but in different scales (nsec, usec, msec, sec).
- **/
-gint64
-nm_utils_get_monotonic_timestamp_us (void)
-{
-	struct timespec tp = { 0 };
-
-	monotonic_timestamp_get (&tp);
-
-	/* Although the result will always be positive, we return a signed
-	 * integer, which makes it easier to calculate time differences (when
-	 * you want to subtract signed values).
-	 **/
-	return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * ((gint64) G_USEC_PER_SEC) +
-	       (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/G_USEC_PER_SEC));
-}
-
-/**
- * nm_utils_get_monotonic_timestamp_ms:
- *
- * Returns: a monotonically increasing time stamp in milliseconds,
- * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME.
- *
- * The returned value will start counting at an undefined point
- * in the past and will always be positive.
- *
- * All the nm_utils_get_monotonic_timestamp_*s functions return the same
- * timestamp but in different scales (nsec, usec, msec, sec).
- **/
-gint64
-nm_utils_get_monotonic_timestamp_ms (void)
-{
-	struct timespec tp = { 0 };
-
-	monotonic_timestamp_get (&tp);
-
-	/* Although the result will always be positive, we return a signed
-	 * integer, which makes it easier to calculate time differences (when
-	 * you want to subtract signed values).
-	 **/
-	return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * ((gint64) 1000) +
-	       (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000));
-}
-
-/**
- * nm_utils_get_monotonic_timestamp_s:
- *
- * Returns: nm_utils_get_monotonic_timestamp_ms() in seconds (throwing
- * away sub second parts). The returned value will always be positive.
- *
- * This value wraps after roughly 68 years which should be fine for any
- * practical purpose.
- *
- * All the nm_utils_get_monotonic_timestamp_*s functions return the same
- * timestamp but in different scales (nsec, usec, msec, sec).
- **/
-gint32
-nm_utils_get_monotonic_timestamp_s (void)
-{
-	struct timespec tp = { 0 };
-
-	monotonic_timestamp_get (&tp);
-	return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec);
-}
-
-typedef struct
-{
-	const char *name;
-	NMSetting *setting;
-	NMSetting *diff_base_setting;
-	GHashTable *setting_diff;
-} LogConnectionSettingData;
-
-typedef struct
-{
-	const char *item_name;
-	NMSettingDiffResult diff_result;
-} LogConnectionSettingItem;
-
-static gint
-_log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b)
-{
-	const LogConnectionSettingData *v1 = a;
-	const LogConnectionSettingData *v2 = b;
-	guint32 p1, p2;
-	NMSetting *s1, *s2;
-
-	s1 = v1->setting ? v1->setting : v1->diff_base_setting;
-	s2 = v2->setting ? v2->setting : v2->diff_base_setting;
-
-	g_assert (s1 && s2);
-
-	p1 = _nm_setting_get_setting_priority (s1);
-	p2 = _nm_setting_get_setting_priority (s2);
-
-	if (p1 != p2)
-		return p1 > p2 ? 1 : -1;
-
-	return strcmp (v1->name, v2->name);
-}
-
-static GArray *
-_log_connection_sort_hashes (NMConnection *connection, NMConnection *diff_base, GHashTable *connection_diff)
-{
-	GHashTableIter iter;
-	GArray *sorted_hashes;
-	LogConnectionSettingData setting_data;
-
-	sorted_hashes = g_array_sized_new (TRUE, FALSE, sizeof (LogConnectionSettingData), g_hash_table_size (connection_diff));
-
-	g_hash_table_iter_init (&iter, connection_diff);
-	while (g_hash_table_iter_next (&iter, (gpointer) &setting_data.name, (gpointer) &setting_data.setting_diff)) {
-		setting_data.setting = nm_connection_get_setting_by_name (connection, setting_data.name);
-		setting_data.diff_base_setting = diff_base ? nm_connection_get_setting_by_name (diff_base, setting_data.name) : NULL;
-		g_assert (setting_data.setting || setting_data.diff_base_setting);
-		g_array_append_val (sorted_hashes, setting_data);
-	}
-
-	g_array_sort (sorted_hashes, _log_connection_sort_hashes_fcn);
-	return sorted_hashes;
-}
-
-static gint
-_log_connection_sort_names_fcn (gconstpointer a, gconstpointer b)
-{
-	const LogConnectionSettingItem *v1 = a;
-	const LogConnectionSettingItem *v2 = b;
-
-	/* we want to first show the items, that disappeared, then the one that changed and
-	 * then the ones that were added. */
-
-	if ((v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A) != (v2->diff_result & NM_SETTING_DIFF_RESULT_IN_A))
-		return (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A) ? -1 : 1;
-	if ((v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B) != (v2->diff_result & NM_SETTING_DIFF_RESULT_IN_B))
-		return (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B) ? 1 : -1;
-	return strcmp (v1->item_name, v2->item_name);
-}
-
-static char *
-_log_connection_get_property (NMSetting *setting, const char *name)
-{
-	GValue val = G_VALUE_INIT;
-	char *s;
-
-	g_return_val_if_fail (setting, NULL);
-
-	if (   !NM_IS_SETTING_VPN (setting)
-	    && nm_setting_get_secret_flags (setting, name, NULL, NULL))
-		return g_strdup ("****");
-
-	if (!_nm_setting_get_property (setting, name, &val))
-		g_return_val_if_reached (FALSE);
-
-	if (G_VALUE_HOLDS_STRING (&val)) {
-		const char *val_s;
-
-		val_s = g_value_get_string (&val);
-		if (!val_s) {
-			/* for NULL, we want to return the unquoted string "NULL". */
-			s = g_strdup ("NULL");
-		} else {
-			char *escaped = g_strescape (val_s, "'");
-
-			s = g_strdup_printf ("'%s'", escaped);
-			g_free (escaped);
-		}
-	} else {
-		s = g_strdup_value_contents (&val);
-		if (s == NULL)
-			s = g_strdup ("NULL");
-		else {
-			char *escaped = g_strescape (s, "'");
-
-			g_free (s);
-			s = escaped;
-		}
-	}
-	g_value_unset(&val);
-	return s;
-}
-
-static void
-_log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sorted_names)
-{
-	GHashTableIter iter;
-	LogConnectionSettingItem item;
-	gpointer p;
-
-	g_array_set_size (sorted_names, 0);
-
-	g_hash_table_iter_init (&iter, setting_data->setting_diff);
-	while (g_hash_table_iter_next (&iter, (gpointer) &item.item_name, &p)) {
-		item.diff_result = GPOINTER_TO_UINT (p);
-		g_array_append_val (sorted_names, item);
-	}
-
-	g_array_sort (sorted_names, _log_connection_sort_names_fcn);
-}
-
-void
-nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix)
-{
-	GHashTable *connection_diff = NULL;
-	GArray *sorted_hashes;
-	GArray *sorted_names = NULL;
-	int i, j;
-	gboolean connection_diff_are_same;
-	gboolean print_header = TRUE;
-	gboolean print_setting_header;
-	GString *str1;
-
-	g_return_if_fail (NM_IS_CONNECTION (connection));
-	g_return_if_fail (!diff_base || (NM_IS_CONNECTION (diff_base) && diff_base != connection));
-
-	/* For VPN setting types, this is broken, because we cannot (generically) print the content of data/secrets. Bummer... */
-
-	if (!nm_logging_enabled (level, domain))
-		return;
-
-	if (!prefix)
-		prefix = "";
-	if (!name)
-		name = "";
-
-	connection_diff_are_same = nm_connection_diff (connection, diff_base, NM_SETTING_COMPARE_FLAG_EXACT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, &connection_diff);
-	if (connection_diff_are_same) {
-		if (diff_base)
-			nm_log (level, domain, "%sconnection '%s' (%p/%s and %p/%s): no difference", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base));
-		else
-			nm_log (level, domain, "%sconnection '%s' (%p/%s): no properties set", prefix, name, connection, G_OBJECT_TYPE_NAME (connection));
-		g_assert (!connection_diff);
-		return;
-	}
-
-	/* FIXME: it doesn't nicely show the content of NMSettingVpn, becuase nm_connection_diff() does not
-	 * expand the hash values. */
-
-	sorted_hashes = _log_connection_sort_hashes (connection, diff_base, connection_diff);
-	if (sorted_hashes->len <= 0)
-		goto out;
-
-	sorted_names = g_array_new (FALSE, FALSE, sizeof (LogConnectionSettingItem));
-	str1 = g_string_new (NULL);
-
-	for (i = 0; i < sorted_hashes->len; i++) {
-		LogConnectionSettingData *setting_data = &g_array_index (sorted_hashes, LogConnectionSettingData, i);
-
-		_log_connection_sort_names (setting_data, sorted_names);
-		print_setting_header = TRUE;
-		for (j = 0; j < sorted_names->len; j++) {
-			char *str_conn, *str_diff;
-			LogConnectionSettingItem *item = &g_array_index (sorted_names, LogConnectionSettingItem, j);
-
-			str_conn = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_A)
-			           ? _log_connection_get_property (setting_data->setting, item->item_name)
-			           : NULL;
-			str_diff = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_B)
-			           ? _log_connection_get_property (setting_data->diff_base_setting, item->item_name)
-			           : NULL;
-
-			if (print_header) {
-				GError *err_verify = NULL;
-				const char *path = nm_connection_get_path (connection);
-
-				if (diff_base) {
-					nm_log (level, domain, "%sconnection '%s' (%p/%s < %p/%s)%s%s%s:", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base),
-					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
-				} else {
-					nm_log (level, domain, "%sconnection '%s' (%p/%s):%s%s%s", prefix, name, connection, G_OBJECT_TYPE_NAME (connection),
-					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
-				}
-				print_header = FALSE;
-
-				if (!nm_connection_verify (connection, &err_verify)) {
-					nm_log (level, domain, "%sconnection %p does not verify: %s", prefix, connection, err_verify->message);
-					g_clear_error (&err_verify);
-				}
-			}
-#define _NM_LOG_ALIGN "-25"
-			if (print_setting_header) {
-				if (diff_base) {
-					if (setting_data->setting && setting_data->diff_base_setting)
-						g_string_printf (str1, "%p < %p", setting_data->setting, setting_data->diff_base_setting);
-					else if (setting_data->diff_base_setting)
-						g_string_printf (str1, "*missing* < %p", setting_data->diff_base_setting);
-					else
-						g_string_printf (str1, "%p < *missing*", setting_data->setting);
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s [ %s ]", prefix, setting_data->name, str1->str);
-				} else
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s [ %p ]", prefix, setting_data->name, setting_data->setting);
-				print_setting_header = FALSE;
-			}
-			g_string_printf (str1, "%s.%s", setting_data->name, item->item_name);
-			switch (item->diff_result & (NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B)) {
-				case NM_SETTING_DIFF_RESULT_IN_B:
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ? str_diff : "NULL");
-					break;
-				case NM_SETTING_DIFF_RESULT_IN_A:
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ? str_conn : "NULL");
-					break;
-				default:
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ? str_conn : "NULL", str_diff ? str_diff : "NULL");
-					break;
-#undef _NM_LOG_ALIGN
-			}
-			g_free (str_conn);
-			g_free (str_diff);
-		}
-	}
-
-	g_array_free (sorted_names, TRUE);
-	g_string_free (str1, TRUE);
-out:
-	g_hash_table_destroy (connection_diff);
-	g_array_free (sorted_hashes, TRUE);
-}
-
-/**
- * nm_utils_monotonic_timestamp_as_boottime:
- * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME.
- * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if
- * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano
- * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc.
- *
- * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime().
- * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick.
- * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds.
- * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp
- * is always positive.
- *
- * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC.
- **/
-gint64
-nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick)
-{
-	gint64 offset;
-
-	/* only support ns-per-tick being a multiple of 10. */
-	g_return_val_if_fail (timestamp_ns_per_tick == 1
-	                      || (timestamp_ns_per_tick > 0 &&
-	                          timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND &&
-	                          timestamp_ns_per_tick % 10 == 0),
-	                      -1);
-
-	/* Check that the timestamp is in a valid range. */
-	g_return_val_if_fail (timestamp >= 0, -1);
-
-	/* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful
-	 * value (because he has no idea what these timestamps would be). That would be a bug. */
-	g_return_val_if_fail (monotonic_timestamp_clock_mode != 0, -1);
-
-	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
-	offset = monotonic_timestamp_offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick);
-
-	/* check for overflow. */
-	g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64);
-
-	return timestamp - offset;
-}
-
-
-#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
-#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
-G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR));
-
-static const char *
-_get_property_path (const char *ifname,
-                    const char *property,
-                    gboolean ipv6)
-{
-	static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
-	int len;
-
-	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
-	property = ASSERT_VALID_PATH_COMPONENT (property);
-
-	len = g_snprintf (path,
-	                  sizeof (path),
-	                  "%s%s/%s",
-	                  ipv6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR,
-	                  ifname,
-	                  property);
-	g_assert (len < sizeof (path) - 1);
-
-	return path;
-}
-
-/**
- * nm_utils_ip6_property_path:
- * @ifname: an interface name
- * @property: a property name
- *
- * Returns the path to IPv6 property @property on @ifname. Note that
- * this uses a static buffer.
- */
-const char *
-nm_utils_ip6_property_path (const char *ifname, const char *property)
-{
-	return _get_property_path (ifname, property, TRUE);
-}
-
-/**
- * nm_utils_ip4_property_path:
- * @ifname: an interface name
- * @property: a property name
- *
- * Returns the path to IPv4 property @property on @ifname. Note that
- * this uses a static buffer.
- */
-const char *
-nm_utils_ip4_property_path (const char *ifname, const char *property)
-{
-	return _get_property_path (ifname, property, FALSE);
-}
-
-gboolean
-nm_utils_is_valid_path_component (const char *name)
-{
-	const char *n;
-
-	if (name == NULL || name[0] == '\0')
-		return FALSE;
-
-	if (name[0] == '.') {
-		if (name[1] == '\0')
-			return FALSE;
-		if (name[1] == '.' && name[2] == '\0')
-			return FALSE;
-	}
-	n = name;
-	do {
-		if (*n == '/')
-			return FALSE;
-	} while (*(++n) != '\0');
-
-	return TRUE;
-}
-
-const char *
-ASSERT_VALID_PATH_COMPONENT (const char *name)
-{
-	if (G_LIKELY (nm_utils_is_valid_path_component (name)))
-		return name;
-
-	nm_log_err (LOGD_CORE, "Failed asserting path component: %s%s%s",
-	            NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)"));
-	g_error ("FATAL: Failed asserting path component: %s%s%s",
-	         NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)"));
-	g_assert_not_reached ();
-}
-
-gboolean
-nm_utils_is_specific_hostname (const char *name)
-{
-	if (!name)
-		return FALSE;
-	if (   strcmp (name, "(none)")
-	    && strcmp (name, "localhost")
-	    && strcmp (name, "localhost6")
-	    && strcmp (name, "localhost.localdomain")
-	    && strcmp (name, "localhost6.localdomain6"))
-		return TRUE;
-	return FALSE;
-}
-
-/******************************************************************/
-
-/* Returns the "u" (universal/local) bit value for a Modified EUI-64 */
-static gboolean
-get_gre_eui64_u_bit (guint32 addr)
-{
-	static const struct {
-		guint32 mask;
-		guint32 result;
-	} items[] = {
-		{ 0xff000000 }, { 0x7f000000 },  /* IPv4 loopback */
-		{ 0xf0000000 }, { 0xe0000000 },  /* IPv4 multicast */
-		{ 0xffffff00 }, { 0xe0000000 },  /* IPv4 local multicast */
-		{ 0xffffffff }, { INADDR_BROADCAST },  /* limited broadcast */
-		{ 0xff000000 }, { 0x00000000 },  /* zero net */
-		{ 0xff000000 }, { 0x0a000000 },  /* private 10 (RFC3330) */
-		{ 0xfff00000 }, { 0xac100000 },  /* private 172 */
-		{ 0xffff0000 }, { 0xc0a80000 },  /* private 192 */
-		{ 0xffff0000 }, { 0xa9fe0000 },  /* IPv4 link-local */
-		{ 0xffffff00 }, { 0xc0586300 },  /* anycast 6-to-4 */
-		{ 0xffffff00 }, { 0xc0000200 },  /* test 192 */
-		{ 0xfffe0000 }, { 0xc6120000 },  /* test 198 */
-	};
-	guint i;
-
-	for (i = 0; i < G_N_ELEMENTS (items); i++) {
-		if ((addr & htonl (items[i].mask)) == htonl (items[i].result))
-			return 0x00; /* "local" scope */
-	}
-	return 0x02; /* "universal" scope */
-}
-
-/**
- * nm_utils_get_ipv6_interface_identifier:
- * @link_type: the hardware link type
- * @hwaddr: the hardware address of the interface
- * @hwaddr_len: the length (in bytes) of @hwaddr
- * @dev_id: the device identifier, if any
- * @out_iid: on success, filled with the interface identifier; on failure
- * zeroed out
- *
- * Constructs an interface identifier in "Modified EUI-64" format which is
- * suitable for constructing IPv6 addresses.  Note that the identifier is
- * not obscured in any way (eg, RFC3041).
- *
- * Returns: %TRUE if the interface identifier could be constructed, %FALSE if
- * if could not be constructed.
- */
-gboolean
-nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
-                                        const guint8 *hwaddr,
-                                        guint hwaddr_len,
-                                        guint dev_id,
-                                        NMUtilsIPv6IfaceId *out_iid)
-{
-	guint32 addr;
-
-	g_return_val_if_fail (hwaddr != NULL, FALSE);
-	g_return_val_if_fail (hwaddr_len > 0, FALSE);
-	g_return_val_if_fail (out_iid != NULL, FALSE);
-
-	out_iid->id = 0;
-
-	switch (link_type) {
-	case NM_LINK_TYPE_INFINIBAND:
-		/* Use the port GUID per http://tools.ietf.org/html/rfc4391#section-8,
-		 * making sure to set the 'u' bit to 1.  The GUID is the lower 64 bits
-		 * of the IPoIB interface's hardware address.
-		 */
-		g_return_val_if_fail (hwaddr_len == INFINIBAND_ALEN, FALSE);
-		memcpy (out_iid->id_u8, hwaddr + INFINIBAND_ALEN - 8, 8);
-		out_iid->id_u8[0] |= 0x02;
-		return TRUE;
-	case NM_LINK_TYPE_GRE:
-	case NM_LINK_TYPE_GRETAP:
-		/* Hardware address is the network-endian IPv4 address */
-		g_return_val_if_fail (hwaddr_len == 4, FALSE);
-		addr = * (guint32 *) hwaddr;
-		out_iid->id_u8[0] = get_gre_eui64_u_bit (addr);
-		out_iid->id_u8[1] = 0x00;
-		out_iid->id_u8[2] = 0x5E;
-		out_iid->id_u8[3] = 0xFE;
-		memcpy (out_iid->id_u8 + 4, &addr, 4);
-		return TRUE;
-	default:
-		if (hwaddr_len == ETH_ALEN) {
-			/* Translate 48-bit MAC address to a 64-bit Modified EUI-64.  See
-			 * http://tools.ietf.org/html/rfc4291#appendix-A and the Linux
-			 * kernel's net/ipv6/addrconf.c::ipv6_generate_eui64() function.
-			 */
-			out_iid->id_u8[0] = hwaddr[0];
-			out_iid->id_u8[1] = hwaddr[1];
-			out_iid->id_u8[2] = hwaddr[2];
-			if (dev_id) {
-				out_iid->id_u8[3] = (dev_id >> 8) & 0xff;
-				out_iid->id_u8[4] = dev_id & 0xff;
-			} else {
-				out_iid->id_u8[0] ^= 0x02;
-				out_iid->id_u8[3] = 0xff;
-				out_iid->id_u8[4] = 0xfe;
-			}
-			out_iid->id_u8[5] = hwaddr[3];
-			out_iid->id_u8[6] = hwaddr[4];
-			out_iid->id_u8[7] = hwaddr[5];
-			return TRUE;
-		}
-		break;
-	}
-	return FALSE;
-}
-void
-nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
-                                            const NMUtilsIPv6IfaceId iid)
-{
-	memcpy (addr->s6_addr + 8, &iid.id_u8, 8);
-}
-
-void
-nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
-                                                 const struct in6_addr *addr)
-{
-	memcpy (iid, addr->s6_addr + 8, 8);
-}
-
-static gboolean
-_set_stable_privacy (struct in6_addr *addr,
-                     const char *ifname,
-                     const char *uuid,
-                     guint dad_counter,
-                     gchar *secret_key,
-                     gsize key_len,
-                     GError **error)
-{
-	GChecksum *sum;
-	guint8 digest[32];
-	guint32 tmp[2];
-	gsize len = sizeof (digest);
-
-	g_return_val_if_fail (key_len, FALSE);
-
-	/* Documentation suggests that this can fail.
-	 * Maybe in case of a missing algorithm in crypto library? */
-	sum = g_checksum_new (G_CHECKSUM_SHA256);
-	if (!sum) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "Can't create a SHA256 hash");
-		return FALSE;
-	}
-
-	key_len = MIN (key_len, G_MAXUINT32);
-
-	g_checksum_update (sum, addr->s6_addr, 8);
-	g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1);
-	if (!uuid)
-		uuid = "";
-	g_checksum_update (sum, (const guchar *) uuid, strlen (uuid) + 1);
-	tmp[0] = htonl (dad_counter);
-	tmp[1] = htonl (key_len);
-	g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp));
-	g_checksum_update (sum, (const guchar *) secret_key, key_len);
-
-	g_checksum_get_digest (sum, digest, &len);
-	g_checksum_free (sum);
-
-	g_return_val_if_fail (len == 32, FALSE);
-
-	memcpy (addr->s6_addr + 8, &digest[0], 8);
-
-	return TRUE;
-}
-
-#define RFC7217_IDGEN_RETRIES 3
-/**
- * nm_utils_ipv6_addr_set_stable_privacy:
- *
- * Extend the address prefix with an interface identifier using the
- * RFC 7217 Stable Privacy mechanism.
- *
- * Returns: %TRUE on success, %FALSE if the address could not be generated.
- */
-gboolean
-nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
-                                       const char *ifname,
-                                       const char *uuid,
-                                       guint dad_counter,
-                                       GError **error)
-{
-	gchar *secret_key = NULL;
-	gsize key_len = 0;
-	gboolean success = FALSE;
-
-	if (dad_counter >= RFC7217_IDGEN_RETRIES) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "Too many DAD collisions");
-		return FALSE;
-	}
-
-	/* Let's try to load a saved secret key first. */
-	if (g_file_get_contents (NMSTATEDIR "/secret_key", &secret_key, &key_len, NULL)) {
-		if (key_len < 16) {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "Key is too short to be usable");
-			key_len = 0;
-		}
-	} else {
-		int urandom = open ("/dev/urandom", O_RDONLY);
-		mode_t key_mask;
-
-		if (!urandom) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "Can't open /dev/urandom: %s", strerror (errno));
-			return FALSE;
-		}
-
-		/* RFC7217 mandates the key SHOULD be at least 128 bits.
-		 * Let's use twice as much. */
-		key_len = 32;
-		secret_key = g_malloc (key_len);
-
-		key_mask = umask (0077);
-		if (read (urandom, secret_key, key_len) == key_len) {
-			if (!g_file_set_contents (NMSTATEDIR "/secret_key", secret_key, key_len, error)) {
-				g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key");
-				key_len = 0;
-			}
-		} else {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "Could not obtain a secret");
-			key_len = 0;
-		}
-		umask (key_mask);
-		close (urandom);
-	}
-
-	if (key_len) {
-		success = _set_stable_privacy (addr, ifname, uuid, dad_counter,
-		                               secret_key, key_len, error);
-	}
-
-	g_free (secret_key);
-	return success;
-}
-
-/**
- * nm_utils_setpgid:
- * @unused: unused
- *
- * This can be passed as a child setup function to the g_spawn*() family
- * of functions, to ensure that the child is in its own process group
- * (and thus, in some situations, will not be killed when NetworkManager
- * is killed).
- */
-void
-nm_utils_setpgid (gpointer unused G_GNUC_UNUSED)
-{
-	pid_t pid;
-
-	pid = getpid ();
-	setpgid (pid, pid);
-}
+/******************************************************************************/
 
 /**
  * nm_utils_g_value_set_object_path:
@@ -3523,23 +785,4 @@ nm_utils_g_value_set_object_path_array (GValue *value,
 	g_value_take_boxed (value, paths);
 }
 
-/**
- * nm_utils_g_value_set_strv:
- * @value: a #GValue, initialized to store a #G_TYPE_STRV
- * @strings: a #GPtrArray of strings
- *
- * Converts @strings to a #GStrv and stores it in @value.
- */
-void
-nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings)
-{
-	char **strv;
-	int i;
-
-	strv = g_new (char *, strings->len + 1);
-	for (i = 0; i < strings->len; i++)
-		strv[i] = g_strdup (strings->pdata[i]);
-	strv[i] = NULL;
-
-	g_value_take_boxed (value, strv);
-}
+/******************************************************************************/