summary refs log tree commit diff
path: root/shared/nm-glib-aux
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
committerMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
commit28028b26b3371756811e95d894f709f4b1207c00 (patch)
tree6fe7316fd743b51042db47601a8ef8814b3134ac /shared/nm-glib-aux
parente22609983008e1a669196ad64ba3a59ae8c76e0d (diff)
New upstream version 1.22.0 upstream/1.22.0
Diffstat (limited to 'shared/nm-glib-aux')
-rw-r--r--shared/nm-glib-aux/nm-c-list.h56
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.c333
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.h152
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.c21
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.h21
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.c24
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.h21
-rw-r--r--shared/nm-glib-aux/nm-errno.c21
-rw-r--r--shared/nm-glib-aux/nm-errno.h21
-rw-r--r--shared/nm-glib-aux/nm-glib.h60
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.c72
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.h57
-rw-r--r--shared/nm-glib-aux/nm-io-utils.c179
-rw-r--r--shared/nm-glib-aux/nm-io-utils.h54
-rw-r--r--shared/nm-glib-aux/nm-jansson.h17
-rw-r--r--shared/nm-glib-aux/nm-json-aux.c18
-rw-r--r--shared/nm-glib-aux/nm-json-aux.h18
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.c38
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.h21
-rw-r--r--shared/nm-glib-aux/nm-logging-base.c38
-rw-r--r--shared/nm-glib-aux/nm-logging-base.h29
-rw-r--r--shared/nm-glib-aux/nm-logging-fwd.h154
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h71
-rw-r--r--shared/nm-glib-aux/nm-obj.h21
-rw-r--r--shared/nm-glib-aux/nm-random-utils.c21
-rw-r--r--shared/nm-glib-aux/nm-random-utils.h21
-rw-r--r--shared/nm-glib-aux/nm-ref-string.c195
-rw-r--r--shared/nm-glib-aux/nm-ref-string.h71
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.c23
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.h21
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c768
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h236
-rw-r--r--shared/nm-glib-aux/nm-time-utils.c85
-rw-r--r--shared/nm-glib-aux/nm-time-utils.h22
-rw-r--r--shared/nm-glib-aux/nm-value-type.h18
35 files changed, 2288 insertions, 710 deletions
diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h
index 7512730d..256dda74 100644
--- a/shared/nm-glib-aux/nm-c-list.h
+++ b/shared/nm-glib-aux/nm-c-list.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2014 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NM_C_LIST_H__
@@ -32,6 +17,12 @@
 		_what && c_list_contains (list, &_what->member); \
 	})
 
+/* iterate over the list backwards. */
+#define nm_c_list_for_each_entry_prev(_iter, _list, _m) \
+	for (_iter = c_list_entry ((_list)->prev, __typeof__ (*_iter), _m); \
+	     &(_iter)->_m != (_list); \
+	     _iter = c_list_entry ((_iter)->_m.prev, __typeof__ (*_iter), _m))
+
 /*****************************************************************************/
 
 typedef struct {
@@ -88,8 +79,25 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn)
 		nm_c_list_elem_free_full (elem, free_fcn);
 }
 
+#define nm_c_list_elem_find_first(head, arg, predicate) \
+	({ \
+		CList *const _head = (head); \
+		NMCListElem *_result = NULL; \
+		NMCListElem *_elem; \
+		\
+		c_list_for_each_entry (_elem, _head, lst) { \
+			void *const arg = _elem->data; \
+			\
+			if (predicate) { \
+				_result = _elem; \
+				break; \
+			} \
+		} \
+		_result; \
+	})
+
 /**
- * nm_c_list_elem_find_first:
+ * nm_c_list_elem_find_first_ptr:
  * @head: the @CList head of a list containing #NMCListElem elements.
  *   Note that the head is not itself part of the list.
  * @needle: the needle pointer.
@@ -100,15 +108,9 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn)
  * Returns: the found list element or %NULL if not found.
  */
 static inline NMCListElem *
-nm_c_list_elem_find_first (CList *head, gconstpointer needle)
+nm_c_list_elem_find_first_ptr (CList *head, gconstpointer needle)
 {
-	NMCListElem *elem;
-
-	c_list_for_each_entry (elem, head, lst) {
-		if (elem->data == needle)
-			return elem;
-	}
-	return NULL;
+	return nm_c_list_elem_find_first (head, x, x == needle);
 }
 
 /*****************************************************************************/
diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c
index 083c4fee..75b282bc 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.c
+++ b/shared/nm-glib-aux/nm-dbus-aux.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -67,3 +52,315 @@ nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection,
 	                        _nm_dbus_connection_call_get_name_owner_cb,
 	                        nm_utils_user_data_pack (user_data, callback));
 }
+
+/*****************************************************************************/
+
+static void
+_nm_dbus_connection_call_get_all_cb (GObject *source,
+                                     GAsyncResult *res,
+                                     gpointer user_data)
+{
+	gs_unref_variant GVariant *ret = NULL;
+	gs_free_error GError *error = NULL;
+	gpointer orig_user_data;
+	NMDBusConnectionCallDefaultCb callback;
+
+	nm_utils_user_data_unpack (user_data, &orig_user_data, &callback);
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+	nm_assert ((!!ret) != (!!error));
+
+	callback (ret, error, orig_user_data);
+}
+
+void
+nm_dbus_connection_call_get_all (GDBusConnection *dbus_connection,
+                                 const char *bus_name,
+                                 const char *object_path,
+                                 const char *interface_name,
+                                 int timeout_msec,
+                                 GCancellable *cancellable,
+                                 NMDBusConnectionCallDefaultCb callback,
+                                 gpointer user_data)
+{
+	nm_assert (callback);
+
+	g_dbus_connection_call (dbus_connection,
+	                        bus_name,
+	                        object_path,
+	                        DBUS_INTERFACE_PROPERTIES,
+	                        "GetAll",
+	                        g_variant_new ("(s)", interface_name),
+	                        G_VARIANT_TYPE ("(a{sv})"),
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        timeout_msec,
+	                        cancellable,
+	                        _nm_dbus_connection_call_get_all_cb,
+	                        nm_utils_user_data_pack (user_data, callback));
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	NMDBusConnectionSignalObjectMangerCb callback;
+	gpointer user_data;
+	GDestroyNotify user_data_free_func;
+} SubscribeObjectManagerData;
+
+static void
+_subscribe_object_manager_cb (GDBusConnection *connection,
+                              const char *sender_name,
+                              const char *arg_object_path,
+                              const char *interface_name,
+                              const char *signal_name,
+                              GVariant *parameters,
+                              gpointer user_data)
+{
+	const SubscribeObjectManagerData *d = user_data;
+
+	nm_assert (nm_streq0 (interface_name, DBUS_INTERFACE_OBJECT_MANAGER));
+
+	if (nm_streq (signal_name, "InterfacesAdded")) {
+		gs_unref_variant GVariant *interfaces_and_properties = NULL;
+		const char *object_path;
+
+		if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oa{sa{sv}})")))
+			return;
+
+		g_variant_get (parameters,
+		               "(&o@a{sa{sv}})",
+		               &object_path,
+		               &interfaces_and_properties);
+
+		d->callback (object_path, interfaces_and_properties, NULL, d->user_data);
+		return;
+	}
+
+	if (nm_streq (signal_name, "InterfacesRemoved")) {
+		gs_free const char **interfaces = NULL;
+		const char *object_path;
+
+		if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oas)")))
+			return;
+
+		g_variant_get (parameters,
+		               "(&o^a&s)",
+		               &object_path,
+		               &interfaces);
+
+		d->callback (object_path, NULL, interfaces, d->user_data);
+		return;
+	}
+}
+
+static void
+_subscribe_object_manager_data_free (gpointer ptr)
+{
+	SubscribeObjectManagerData *d = ptr;
+
+	if (d->user_data_free_func)
+		d->user_data_free_func (d->user_data);
+	nm_g_slice_free (d);
+}
+
+guint
+nm_dbus_connection_signal_subscribe_object_manager (GDBusConnection *dbus_connection,
+                                                    const char *service_name,
+                                                    const char *object_path,
+                                                    NMDBusConnectionSignalObjectMangerCb callback,
+                                                    gpointer user_data,
+                                                    GDestroyNotify user_data_free_func)
+{
+	SubscribeObjectManagerData *d;
+
+	g_return_val_if_fail (callback, 0);
+
+	d = g_slice_new (SubscribeObjectManagerData);
+	*d = (SubscribeObjectManagerData) {
+		.callback            = callback,
+		.user_data           = user_data,
+		.user_data_free_func = user_data_free_func,
+	};
+
+	return nm_dbus_connection_signal_subscribe_object_manager_plain (dbus_connection,
+	                                                                 service_name,
+	                                                                 object_path,
+	                                                                 NULL,
+	                                                                 _subscribe_object_manager_cb,
+	                                                                 d,
+	                                                                 _subscribe_object_manager_data_free);
+}
+
+/*****************************************************************************/
+
+static void
+_nm_dbus_connection_call_get_managed_objects_cb (GObject *source,
+                                                 GAsyncResult *res,
+                                                 gpointer user_data)
+{
+	gs_unref_variant GVariant *ret = NULL;
+	gs_unref_variant GVariant *arg = NULL;
+	gs_free_error GError *error = NULL;
+	gpointer orig_user_data;
+	NMDBusConnectionCallDefaultCb callback;
+
+	nm_utils_user_data_unpack (user_data, &orig_user_data, &callback);
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+	nm_assert ((!!ret) != (!!error));
+
+	if (ret) {
+		nm_assert (g_variant_is_of_type (ret, G_VARIANT_TYPE ("(a{oa{sa{sv}}})")));
+		arg = g_variant_get_child_value (ret, 0);
+	}
+
+	callback (arg, error, orig_user_data);
+}
+
+void
+nm_dbus_connection_call_get_managed_objects (GDBusConnection *dbus_connection,
+                                             const char *bus_name,
+                                             const char *object_path,
+                                             GDBusCallFlags flags,
+                                             int timeout_msec,
+                                             GCancellable *cancellable,
+                                             NMDBusConnectionCallDefaultCb callback,
+                                             gpointer user_data)
+{
+	nm_assert (callback);
+
+	g_dbus_connection_call (dbus_connection,
+	                        bus_name,
+	                        object_path,
+	                        DBUS_INTERFACE_OBJECT_MANAGER,
+	                        "GetManagedObjects",
+	                        NULL,
+	                        G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
+	                        flags,
+	                        timeout_msec,
+	                        cancellable,
+	                        _nm_dbus_connection_call_get_managed_objects_cb,
+	                        nm_utils_user_data_pack (user_data, callback));
+}
+
+/*****************************************************************************/
+
+static void
+_call_finish_cb (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data,
+                 gboolean return_void,
+                 gboolean strip_dbus_error)
+{
+	gs_unref_object GTask *task = user_data;
+	gs_unref_variant GVariant *ret = NULL;
+	GError *error = NULL;
+
+	nm_assert (G_IS_DBUS_CONNECTION (source));
+	nm_assert (G_IS_TASK (user_data));
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+	if (!ret) {
+		if (strip_dbus_error)
+			g_dbus_error_strip_remote_error (error);
+		g_task_return_error (task, error);
+		return;
+	}
+
+	if (!return_void) {
+		nm_assert (!g_variant_is_of_type (ret, G_VARIANT_TYPE ("()")));
+		g_task_return_pointer (task, g_steal_pointer (&ret), (GDestroyNotify) g_variant_unref);
+	} else {
+		nm_assert (g_variant_is_of_type (ret, G_VARIANT_TYPE ("()")));
+		g_task_return_boolean (task, TRUE);
+	}
+}
+
+/**
+ * nm_dbus_connection_call_finish_void_cb:
+ *
+ * A default callback to pass as callback to g_dbus_connection_call().
+ *
+ * - user_data must be a GTask, whose reference will be consumed by the
+ *   callback.
+ * - the return GVariant must be a empty tuple "()".
+ * - the GTask is returned either with error or TRUE boolean.
+ */
+void
+nm_dbus_connection_call_finish_void_cb (GObject *source,
+                                        GAsyncResult *result,
+                                        gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, TRUE, FALSE);
+}
+
+/**
+ * nm_dbus_connection_call_finish_void_strip_dbus_error_cb:
+ *
+ * Like nm_dbus_connection_call_finish_void_cb(). The difference
+ * is that on error this will first call g_dbus_error_strip_remote_error() on the error.
+ */
+void
+nm_dbus_connection_call_finish_void_strip_dbus_error_cb (GObject *source,
+                                                         GAsyncResult *result,
+                                                         gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, TRUE, TRUE);
+}
+
+/**
+ * nm_dbus_connection_call_finish_variant_cb:
+ *
+ * A default callback to pass as callback to g_dbus_connection_call().
+ *
+ * - user_data must be a GTask, whose reference will be consumed by the
+ *   callback.
+ * - the return GVariant must not be an empty tuple "()".
+ * - the GTask is returned either with error or with a pointer containing the GVariant.
+ */
+void
+nm_dbus_connection_call_finish_variant_cb (GObject *source,
+                                           GAsyncResult *result,
+                                           gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, FALSE, FALSE);
+}
+
+/**
+ * nm_dbus_connection_call_finish_variant_strip_dbus_error_cb:
+ *
+ * Like nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(). The difference
+ * is that on error this will first call g_dbus_error_strip_remote_error() on the error.
+ */
+void
+nm_dbus_connection_call_finish_variant_strip_dbus_error_cb (GObject *source,
+                                                            GAsyncResult *result,
+                                                            gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, FALSE, TRUE);
+}
+
+/*****************************************************************************/
+
+gboolean
+_nm_dbus_error_is (GError *error, ...)
+{
+	gs_free char *dbus_error = NULL;
+	const char *name;
+	va_list ap;
+
+	dbus_error = g_dbus_error_get_remote_error (error);
+	if (!dbus_error)
+		return FALSE;
+
+	va_start (ap, error);
+	while ((name = va_arg (ap, const char *))) {
+		if (nm_streq (dbus_error, name))
+			return TRUE;
+	}
+	va_end (ap);
+
+	return FALSE;
+}
diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h
index 271a7d9c..840e23c2 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.h
+++ b/shared/nm-glib-aux/nm-dbus-aux.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_DBUS_AUX_H__
@@ -42,6 +27,12 @@ nm_clear_g_dbus_connection_signal (GDBusConnection *dbus_connection,
 
 /*****************************************************************************/
 
+typedef void (*NMDBusConnectionCallDefaultCb) (GVariant *result,
+                                               GError *error,
+                                               gpointer user_data);
+
+/*****************************************************************************/
+
 static inline void
 nm_dbus_connection_call_start_service_by_name (GDBusConnection *dbus_connection,
                                                const char *name,
@@ -91,11 +82,126 @@ typedef void (*NMDBusConnectionCallGetNameOwnerCb) (const char *name_owner,
                                                     gpointer user_data);
 
 void nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection,
-                                              const char *service_name,
-                                              int timeout_msec,
-                                              GCancellable *cancellable,
-                                              NMDBusConnectionCallGetNameOwnerCb callback,
-                                              gpointer user_data);
+                                             const char *service_name,
+                                             int timeout_msec,
+                                             GCancellable *cancellable,
+                                             NMDBusConnectionCallGetNameOwnerCb callback,
+                                             gpointer user_data);
+
+static inline guint
+nm_dbus_connection_signal_subscribe_properties_changed (GDBusConnection *dbus_connection,
+                                                        const char *bus_name,
+                                                        const char *object_path,
+                                                        const char *interface_name,
+                                                        GDBusSignalCallback callback,
+                                                        gpointer user_data,
+                                                        GDestroyNotify user_data_free_func)
+
+{
+	nm_assert (bus_name);
+
+	/* it seems that using a non-unique name causes problems that we get signals
+	 * also from unrelated senders. Usually, you are anyway monitoring the name-owner,
+	 * so you should have the unique name at hand.
+	 *
+	 * If not, investigate this, ensure that it works, and lift this restriction. */
+	nm_assert (g_dbus_is_unique_name (bus_name));
+
+	return g_dbus_connection_signal_subscribe (dbus_connection,
+	                                           bus_name,
+	                                           DBUS_INTERFACE_PROPERTIES,
+	                                           "PropertiesChanged",
+	                                           object_path,
+	                                           interface_name,
+	                                           G_DBUS_SIGNAL_FLAGS_NONE,
+	                                           callback,
+	                                           user_data,
+	                                           user_data_free_func);
+}
+
+void nm_dbus_connection_call_get_all (GDBusConnection *dbus_connection,
+                                      const char *bus_name,
+                                      const char *object_path,
+                                      const char *interface_name,
+                                      int timeout_msec,
+                                      GCancellable *cancellable,
+                                      NMDBusConnectionCallDefaultCb callback,
+                                      gpointer user_data);
+
+/*****************************************************************************/
+
+static inline guint
+nm_dbus_connection_signal_subscribe_object_manager_plain (GDBusConnection *dbus_connection,
+                                                          const char *service_name,
+                                                          const char *object_path,
+                                                          const char *signal_name,
+                                                          GDBusSignalCallback callback,
+                                                          gpointer user_data,
+                                                          GDestroyNotify user_data_free_func)
+{
+	return g_dbus_connection_signal_subscribe (dbus_connection,
+	                                           service_name,
+	                                           DBUS_INTERFACE_OBJECT_MANAGER,
+	                                           signal_name,
+	                                           object_path,
+	                                           NULL,
+	                                           G_DBUS_SIGNAL_FLAGS_NONE,
+	                                           callback,
+	                                           user_data,
+	                                           user_data_free_func);
+}
+
+typedef void (*NMDBusConnectionSignalObjectMangerCb) (const char *object_path,
+                                                      GVariant *added_interfaces_and_properties,
+                                                      const char *const*removed_interfaces,
+                                                      gpointer user_data);
+
+guint nm_dbus_connection_signal_subscribe_object_manager (GDBusConnection *dbus_connection,
+                                                          const char *service_name,
+                                                          const char *object_path,
+                                                          NMDBusConnectionSignalObjectMangerCb callback,
+                                                          gpointer user_data,
+                                                          GDestroyNotify user_data_free_func);
+
+void nm_dbus_connection_call_get_managed_objects (GDBusConnection *dbus_connection,
+                                                  const char *bus_name,
+                                                  const char *object_path,
+                                                  GDBusCallFlags flags,
+                                                  int timeout_msec,
+                                                  GCancellable *cancellable,
+                                                  NMDBusConnectionCallDefaultCb callback,
+                                                  gpointer user_data);
+
+/*****************************************************************************/
+
+void nm_dbus_connection_call_finish_void_cb (GObject *source,
+                                             GAsyncResult *result,
+                                             gpointer user_data);
+
+void nm_dbus_connection_call_finish_void_strip_dbus_error_cb (GObject *source,
+                                                              GAsyncResult *result,
+                                                              gpointer user_data);
+
+void nm_dbus_connection_call_finish_variant_cb (GObject *source,
+                                                GAsyncResult *result,
+                                                gpointer user_data);
+
+void nm_dbus_connection_call_finish_variant_strip_dbus_error_cb (GObject *source,
+                                                                 GAsyncResult *result,
+                                                                 gpointer user_data);
+
+/*****************************************************************************/
+
+gboolean _nm_dbus_error_is (GError *error, ...) G_GNUC_NULL_TERMINATED;
+
+#define nm_dbus_error_is(error, ...) \
+	({ \
+		GError *const _error = (error); \
+		\
+		_error && _nm_dbus_error_is (_error, __VA_ARGS__, NULL); \
+	})
+
+#define NM_DBUS_ERROR_NAME_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod"
 
 /*****************************************************************************/
 
diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c
index 345062ca..6e23228e 100644
--- a/shared/nm-glib-aux/nm-dedup-multi.c
+++ b/shared/nm-glib-aux/nm-dedup-multi.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h
index ca15c516..6867ae79 100644
--- a/shared/nm-glib-aux/nm-dedup-multi.h
+++ b/shared/nm-glib-aux/nm-dedup-multi.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_DEDUP_MULTI_H__
diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c
index b16267a5..e105a4f5 100644
--- a/shared/nm-glib-aux/nm-enum-utils.c
+++ b/shared/nm-glib-aux/nm-enum-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -225,7 +210,7 @@ _nm_utils_enum_from_str_full (GType type,
                               char **err_token,
                               const NMUtilsEnumValueInfo *value_infos)
 {
-	GTypeClass *klass;
+	nm_auto_unref_gtypeclass GTypeClass *klass = NULL;
 	gboolean ret = FALSE;
 	int value = 0;
 	gs_free char *str_clone = NULL;
@@ -317,7 +302,6 @@ _nm_utils_enum_from_str_full (GType type,
 
 	NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL);
 	NM_SET_OUT (out_value, ret ? value : 0);
-	g_type_class_unref (klass);
 	return ret;
 }
 
diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h
index 20db07cc..d863dabd 100644
--- a/shared/nm-glib-aux/nm-enum-utils.h
+++ b/shared/nm-glib-aux/nm-enum-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_ENUM_UTILS_H__
diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c
index 30eb9a8e..9133d87d 100644
--- a/shared/nm-glib-aux/nm-errno.c
+++ b/shared/nm-glib-aux/nm-errno.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-errno.h b/shared/nm-glib-aux/nm-errno.h
index d77735a7..1d329b25 100644
--- a/shared/nm-glib-aux/nm-errno.h
+++ b/shared/nm-glib-aux/nm-errno.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_ERRNO_H__
diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h
index bdb7ea5b..dfb75bf0 100644
--- a/shared/nm-glib-aux/nm-glib.h
+++ b/shared/nm-glib-aux/nm-glib.h
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2008 - 2018 Red Hat, Inc.
+ * Copyright (C) 2008 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_GLIB_H__
@@ -569,6 +556,49 @@ _nm_g_value_unset (GValue *value)
 #define g_value_unset _nm_g_value_unset
 #endif
 
+/* G_PID_FORMAT was added only in 2.53.5. Define it ourself.
+ *
+ * If this was about "pid_t", we would check SIZEOF_PID_T, and set
+ * PRIi32/PRIi16, like systemd does. But it's actually about
+ * GPid, which glib typedefs as an "int".
+ *
+ * There is a test_gpid() that check that GPid is really a typedef
+ * for int. */
+#undef G_PID_FORMAT
+#define G_PID_FORMAT "i"
+
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION (2, 57, 2)
+#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))
+#endif
+
+/*****************************************************************************/
+
+/* Glib implements g_atomic_pointer_compare_and_exchange() as a macro.
+ * For one, to inline the atomic operation and also to perform some type checks
+ * on the arguments.
+ * Depending on compiler and glib version, glib passes the arguments as they
+ * are to __atomic_compare_exchange_n(). Some clang version don't accept const
+ * pointers there. Reimplement the macro to get that right, but with stronger
+ * type checks (as we use typeof()). Had one job. */
+static inline gboolean
+_g_atomic_pointer_compare_and_exchange (volatile void *atomic,
+                                        gconstpointer oldval,
+                                        gconstpointer newval)
+{
+	return g_atomic_pointer_compare_and_exchange ((void **) atomic, (void *) oldval, (void *) newval);
+}
+#undef g_atomic_pointer_compare_and_exchange
+#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
+	({ \
+		typeof (atomic) const _atomic = (atomic); \
+		typeof (*_atomic) const _oldval = (oldval); \
+		typeof (*_atomic) const _newval = (newval); \
+		\
+		_g_atomic_pointer_compare_and_exchange (_atomic, _oldval, _newval); \
+	})
+
 /*****************************************************************************/
 
 #endif  /* __NM_GLIB_H__ */
diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c
index a6158269..cd51bbaf 100644
--- a/shared/nm-glib-aux/nm-hash-utils.c
+++ b/shared/nm-glib-aux/nm-hash-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -201,6 +186,26 @@ nm_pstr_equal (gconstpointer a, gconstpointer b)
 }
 
 guint
+nm_pint_hash (gconstpointer p)
+{
+	const int *s = p;
+
+	if (!s)
+		return nm_hash_static (298377461u);
+	return nm_hash_val (1208815757u, *s);
+}
+
+gboolean
+nm_pint_equals (gconstpointer a, gconstpointer b)
+{
+	const int *s1 = a;
+	const int *s2 = a;
+
+	return    s1 == s2
+	       || (s1 && s2 && *s1 == *s2);
+}
+
+guint
 nm_pdirect_hash (gconstpointer p)
 {
 	const void *const*s = p;
@@ -221,3 +226,34 @@ nm_pdirect_equal (gconstpointer a, gconstpointer b)
 	           && s2
 	           && *s1 == *s2);
 }
+
+guint
+nm_ppdirect_hash (gconstpointer p)
+{
+	const void *const*const*s = p;
+
+	if (!s)
+		return nm_hash_static (396534869u);
+	if (!*s)
+		return nm_hash_static (1476102263u);
+	return nm_direct_hash (**s);
+}
+
+gboolean
+nm_ppdirect_equal (gconstpointer a, gconstpointer b)
+{
+	const void *const*const*s1 = a;
+	const void *const*const*s2 = b;
+
+	if (s1 == s2)
+		return TRUE;
+	if (!s1 || !s2)
+		return FALSE;
+
+	if (*s1 == *s2)
+		return TRUE;
+	if (!*s1 || !*s2)
+		return FALSE;
+
+	return **s1 == **s2;
+}
diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h
index aa0a3d7c..21c5e584 100644
--- a/shared/nm-glib-aux/nm-hash-utils.h
+++ b/shared/nm-glib-aux/nm-hash-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_HASH_UTILS_H__
@@ -293,13 +278,29 @@ gboolean nm_pstr_equal (gconstpointer a, gconstpointer b);
 
 /*****************************************************************************/
 
+/* nm_pint_*() are for hashing keys that are pointers to int values,
+ * that is, "const int *" types. */
+
+guint nm_pint_hash (gconstpointer p);
+gboolean nm_pint_equals (gconstpointer a, gconstpointer b);
+
+/*****************************************************************************/
+
 /* this hashes/compares the pointer value that we point to. Basically,
- * (((const void *const*) a) == ((const void *const*) b)). */
+ * (*((const void *const*) a) == *((const void *const*) b)). */
 
 guint nm_pdirect_hash (gconstpointer p);
 
 gboolean nm_pdirect_equal (gconstpointer a, gconstpointer b);
 
+/* this hashes/compares the direct pointer value by following pointers to
+ * pointers 2 times.
+ * (**((const void *const*const*) a) == **((const void *const*const*) b)). */
+
+guint nm_ppdirect_hash (gconstpointer p);
+
+gboolean nm_ppdirect_equal (gconstpointer a, gconstpointer b);
+
 /*****************************************************************************/
 
 #define NM_HASH_OBFUSCATE_PTR_FMT "%016" G_GINT64_MODIFIER "x"
@@ -311,15 +312,15 @@ gboolean nm_pdirect_equal (gconstpointer a, gconstpointer b);
  *
  * Note that there is a chance that two different pointer values hash to the same obfuscated
  * value. So beware of that when reviewing logs. However, such a collision is very unlikely. */
-#define nm_hash_obfuscate_ptr(static_seed, val) \
-	({ \
-		NMHashState _h; \
-		const void *_val_obf_ptr = (val); \
-		\
-		nm_hash_init (&_h, (static_seed)); \
-		nm_hash_update_val (&_h, _val_obf_ptr); \
-		nm_hash_complete_u64 (&_h); \
-	})
+static inline guint64
+nm_hash_obfuscate_ptr (guint static_seed, gconstpointer val)
+{
+	NMHashState h;
+
+	nm_hash_init (&h, static_seed);
+	nm_hash_update_val (&h, val);
+	return nm_hash_complete_u64 (&h);
+}
 
 /* if you want to log obfuscated pointer for a certain context (like, NMPRuleManager
  * logging user-tags), then you are advised to use nm_hash_obfuscate_ptr() with your
diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c
index 23133ec5..ec0cd4c8 100644
--- a/shared/nm-glib-aux/nm-io-utils.c
+++ b/shared/nm-glib-aux/nm-io-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -32,9 +17,9 @@
 
 /*****************************************************************************/
 
-_nm_printf (3, 4)
+_nm_printf (4, 5)
 static int
-_get_contents_error (GError **error, int errsv, const char *format, ...)
+_get_contents_error (GError **error, int errsv, int *out_errsv, const char *format, ...)
 {
 	nm_assert (NM_ERRNO_NATIVE (errsv));
 
@@ -53,13 +38,17 @@ _get_contents_error (GError **error, int errsv, const char *format, ...)
 		             msg,
 		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
 	}
-	return -errsv;
+
+	nm_assert (errsv > 0);
+	NM_SET_OUT (out_errsv, errsv);
+
+	return FALSE;
 }
-#define _get_contents_error_errno(error, ...) \
+#define _get_contents_error_errno(error, out_errsv, ...) \
 	({ \
 		int _errsv = (errno); \
 		\
-		_get_contents_error (error, _errsv, __VA_ARGS__); \
+		_get_contents_error (error, _errsv, out_errsv, __VA_ARGS__); \
 	})
 
 static char *
@@ -110,21 +99,25 @@ _mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len)
  *  the NUL byte. That is, it reads only files up to a length of
  *  @max_length - 1 bytes.
  * @length: optional output argument of the read file size.
+ * @out_errsv: (allow-none) (out): on error, a positive errno. or zero.
+ * @error:
+ *
  *
  * A reimplementation of g_file_get_contents() with a few differences:
  *   - accepts an open fd, instead of a path name. This allows you to
  *     use openat().
  *   - limits the maximum filesize to max_length.
  *
- * Returns: a negative error code on failure.
+ * Returns: TRUE on success.
  */
-int
+gboolean
 nm_utils_fd_get_contents (int fd,
                           gboolean close_fd,
                           gsize max_length,
                           NMUtilsFileGetContentsFlags flags,
                           char **contents,
                           gsize *length,
+                          int *out_errsv,
                           GError **error)
 {
 	nm_auto_close int fd_keeper = close_fd ? fd : -1;
@@ -133,12 +126,14 @@ nm_utils_fd_get_contents (int fd,
 	const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET);
 	int errsv;
 
-	g_return_val_if_fail (fd >= 0, -EINVAL);
-	g_return_val_if_fail (contents, -EINVAL);
-	g_return_val_if_fail (!error || !*error, -EINVAL);
+	g_return_val_if_fail (fd >= 0, FALSE);
+	g_return_val_if_fail (contents && !*contents, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	NM_SET_OUT (length, 0);
 
 	if (fstat (fd, &stat_buf) < 0)
-		return _get_contents_error_errno (error, "failure during fstat");
+		return _get_contents_error_errno (error, out_errsv, "failure during fstat");
 
 	if (!max_length) {
 		/* default to a very large size, but not extreme */
@@ -151,23 +146,23 @@ nm_utils_fd_get_contents (int fd,
 		ssize_t n_read;
 
 		if (n_stat > max_length - 1)
-			return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
+			return _get_contents_error (error, EMSGSIZE, out_errsv, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
 
 		str = g_try_malloc (n_stat + 1);
 		if (!str)
-			return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat);
+			return _get_contents_error (error, ENOMEM, out_errsv, "failure to allocate buffer of %zu+1 bytes", n_stat);
 
 		n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE);
 		if (n_read < 0) {
 			if (do_bzero_mem)
 				nm_explicit_bzero (str, n_stat);
-			return _get_contents_error (error, -n_read, "error reading %zu bytes from file descriptor", n_stat);
+			return _get_contents_error (error, -n_read, out_errsv, "error reading %zu bytes from file descriptor", n_stat);
 		}
 		str[n_read] = '\0';
 
 		if (n_read < n_stat) {
 			if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1)))
-				return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1);
+				return _get_contents_error (error, ENOMEM, out_errsv, "failure to reallocate buffer with %zu bytes", n_read + 1);
 		}
 		NM_SET_OUT (length, n_read);
 	} else {
@@ -181,13 +176,13 @@ nm_utils_fd_get_contents (int fd,
 		else {
 			fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
 			if (fd2 < 0)
-				return _get_contents_error_errno (error, "error during dup");
+				return _get_contents_error_errno (error, out_errsv, "error during dup");
 		}
 
 		if (!(f = fdopen (fd2, "r"))) {
 			errsv = errno;
 			nm_close (fd2);
-			return _get_contents_error (error, errsv, "failure during fdopen");
+			return _get_contents_error (error, errsv, out_errsv, "failure during fdopen");
 		}
 
 		n_have = 0;
@@ -201,14 +196,14 @@ nm_utils_fd_get_contents (int fd,
 			if (ferror (f)) {
 				if (do_bzero_mem)
 					nm_explicit_bzero (buf, sizeof (buf));
-				return _get_contents_error (error, errsv, "error during fread");
+				return _get_contents_error (error, errsv, out_errsv, "error during fread");
 			}
 
 			if (   n_have > G_MAXSIZE - 1 - n_read
 			    || n_have + n_read + 1 > max_length) {
 				if (do_bzero_mem)
 					nm_explicit_bzero (buf, sizeof (buf));
-				return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
+				return _get_contents_error (error, EMSGSIZE, out_errsv, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
 				                            (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read,
 				                            max_length);
 			}
@@ -230,7 +225,7 @@ nm_utils_fd_get_contents (int fd,
 				if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) {
 					if (do_bzero_mem)
 						nm_explicit_bzero (buf, sizeof (buf));
-					return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc);
+					return _get_contents_error (error, ENOMEM, out_errsv, "failure to allocate buffer of %zu bytes", n_alloc);
 				}
 			}
 
@@ -247,7 +242,7 @@ nm_utils_fd_get_contents (int fd,
 			str[n_have] = '\0';
 			if (n_have + 1 < n_alloc) {
 				if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1)))
-					return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1);
+					return _get_contents_error (error, ENOMEM, out_errsv, "failure to truncate buffer to %zu bytes", n_have + 1);
 			}
 		}
 
@@ -255,7 +250,8 @@ nm_utils_fd_get_contents (int fd,
 	}
 
 	*contents = g_steal_pointer (&str);
-	return 0;
+	NM_SET_OUT (out_errsv, 0);
+	return TRUE;
 }
 
 /**
@@ -270,54 +266,49 @@ nm_utils_fd_get_contents (int fd,
  *   the NUL byte. That is, it reads only files up to a length of
  *   @max_length - 1 bytes.
  * @length: optional output argument of the read file size.
+ * @out_errsv: (allow-none) (out): on error, a positive errno. or zero.
+ * @error:
  *
  * A reimplementation of g_file_get_contents() with a few differences:
  *   - accepts an @dirfd to open @filename relative to that path via openat().
  *   - limits the maximum filesize to max_length.
  *   - uses O_CLOEXEC on internal file descriptor
+ *   - optionally returns the native errno on failure.
  *
- * Returns: a negative error code on failure.
+ * Returns: TRUE on success.
  */
-int
+gboolean
 nm_utils_file_get_contents (int dirfd,
                             const char *filename,
                             gsize max_length,
                             NMUtilsFileGetContentsFlags flags,
                             char **contents,
                             gsize *length,
+                            int *out_errsv,
                             GError **error)
 {
 	int fd;
-	int errsv;
-	char bstrerr[NM_STRERROR_BUFSIZE];
 
-	g_return_val_if_fail (filename && filename[0], -EINVAL);
+	g_return_val_if_fail (filename && filename[0], FALSE);
+	g_return_val_if_fail (contents && !*contents, FALSE);
+
+	NM_SET_OUT (length, 0);
 
 	if (dirfd >= 0) {
 		fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC);
 		if (fd < 0) {
-			errsv = errno;
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "Failed to open file \"%s\" with openat: %s",
-			             filename,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return -NM_ERRNO_NATIVE (errsv);
+			return _get_contents_error_errno (error,
+			                                  out_errsv,
+			                                  "Failed to open file \"%s\" with openat",
+			                                  filename);
 		}
 	} else {
 		fd = open (filename, O_RDONLY | O_CLOEXEC);
 		if (fd < 0) {
-			errsv = errno;
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "Failed to open file \"%s\": %s",
-			             filename,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return -NM_ERRNO_NATIVE (errsv);
+			return _get_contents_error_errno (error,
+			                                  out_errsv,
+			                                  "Failed to open file \"%s\"",
+			                                  filename);
 		}
 	}
 	return nm_utils_fd_get_contents (fd,
@@ -326,6 +317,7 @@ nm_utils_file_get_contents (int dirfd,
 	                                 flags,
 	                                 contents,
 	                                 length,
+	                                 out_errsv,
 	                                 error);
 }
 
@@ -340,6 +332,7 @@ nm_utils_file_set_contents (const char *filename,
                             const char *contents,
                             gssize length,
                             mode_t mode,
+                            int *out_errsv,
                             GError **error)
 {
 	gs_free char *tmp_name = NULL;
@@ -347,7 +340,6 @@ nm_utils_file_set_contents (const char *filename,
 	int errsv;
 	gssize s;
 	int fd;
-	char bstrerr[NM_STRERROR_BUFSIZE];
 
 	g_return_val_if_fail (filename, FALSE);
 	g_return_val_if_fail (contents || !length, FALSE);
@@ -360,33 +352,26 @@ nm_utils_file_set_contents (const char *filename,
 	tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
 	fd = g_mkstemp_full (tmp_name, O_RDWR | O_CLOEXEC, mode);
 	if (fd < 0) {
-		errsv = errno;
-		g_set_error (error,
-		             G_FILE_ERROR,
-		             g_file_error_from_errno (errsv),
-		             "failed to create file %s: %s",
-		             tmp_name,
-		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-		return FALSE;
+		return _get_contents_error_errno (error,
+		                                  out_errsv,
+		                                  "failed to create file %s",
+		                                  tmp_name);
 	}
 
 	while (length > 0) {
 		s = write (fd, contents, length);
 		if (s < 0) {
-			errsv = errno;
+			errsv = NM_ERRNO_NATIVE (errno);
 			if (errsv == EINTR)
 				continue;
 
 			nm_close (fd);
 			unlink (tmp_name);
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "failed to write to file %s: %s",
-			             tmp_name,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return FALSE;
+			return _get_contents_error (error,
+			                            errsv,
+			                            out_errsv,
+			                            "failed to write to file %s",
+			                            tmp_name);
 		}
 
 		g_assert (s <= length);
@@ -404,34 +389,28 @@ nm_utils_file_set_contents (const char *filename,
 	if (   lstat (filename, &statbuf) == 0
 	    && statbuf.st_size > 0) {
 		if (fsync (fd) != 0) {
-			errsv = errno;
-
+			errsv = NM_ERRNO_NATIVE (errno);
 			nm_close (fd);
 			unlink (tmp_name);
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "failed to fsync %s: %s",
-			             tmp_name,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return FALSE;
+			return _get_contents_error (error,
+			                            errsv,
+			                            out_errsv,
+			                            "failed to fsync %s",
+			                            tmp_name);
 		}
 	}
 
 	nm_close (fd);
 
 	if (rename (tmp_name, filename)) {
-		errsv = errno;
+		errsv = NM_ERRNO_NATIVE (errno);
 		unlink (tmp_name);
-		g_set_error (error,
-		             G_FILE_ERROR,
-		             g_file_error_from_errno (errsv),
-		             "failed to rename %s to %s: %s",
-		             tmp_name,
-		             filename,
-		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-		return FALSE;
+		return _get_contents_error (error,
+		                            errsv,
+		                            out_errsv,
+		                            "failed rename %s to %s",
+		                            tmp_name,
+		                            filename);
 	}
 
 	return TRUE;
diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h
index 121fc481..18dc5f74 100644
--- a/shared/nm-glib-aux/nm-io-utils.h
+++ b/shared/nm-glib-aux/nm-io-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_IO_UTILS_H__
@@ -37,26 +22,29 @@ typedef enum {
 	NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0),
 } NMUtilsFileGetContentsFlags;
 
-int nm_utils_fd_get_contents (int fd,
-                              gboolean close_fd,
-                              gsize max_length,
-                              NMUtilsFileGetContentsFlags flags,
-                              char **contents,
-                              gsize *length,
-                              GError **error);
-
-int nm_utils_file_get_contents (int dirfd,
-                                const char *filename,
-                                gsize max_length,
-                                NMUtilsFileGetContentsFlags flags,
-                                char **contents,
-                                gsize *length,
-                                GError **error);
+gboolean nm_utils_fd_get_contents (int fd,
+                                   gboolean close_fd,
+                                   gsize max_length,
+                                   NMUtilsFileGetContentsFlags flags,
+                                   char **contents,
+                                   gsize *length,
+                                   int *out_errsv,
+                                   GError **error);
+
+gboolean nm_utils_file_get_contents (int dirfd,
+                                     const char *filename,
+                                     gsize max_length,
+                                     NMUtilsFileGetContentsFlags flags,
+                                     char **contents,
+                                     gsize *length,
+                                     int *out_errsv,
+                                     GError **error);
 
 gboolean nm_utils_file_set_contents (const char *filename,
                                      const char *contents,
                                      gssize length,
                                      mode_t mode,
+                                     int *out_errsv,
                                      GError **error);
 
 struct stat;
diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h
index d4642319..0a75cff5 100644
--- a/shared/nm-glib-aux/nm-jansson.h
+++ b/shared/nm-glib-aux/nm-jansson.h
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_JANSSON_H__
diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c
index 6f04ef2b..a738ab72 100644
--- a/shared/nm-glib-aux/nm-json-aux.c
+++ b/shared/nm-glib-aux/nm-json-aux.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h
index 19d43ce4..ed3be376 100644
--- a/shared/nm-glib-aux/nm-json-aux.h
+++ b/shared/nm-glib-aux/nm-json-aux.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_JSON_AUX_H__
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c
index 989c773f..8b2af7a8 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.c
+++ b/shared/nm-glib-aux/nm-keyfile-aux.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -186,7 +171,6 @@ nm_key_file_db_destroy (NMKeyFileDB *self)
 void
 nm_key_file_db_start (NMKeyFileDB *self)
 {
-	int r;
 	gs_free char *contents = NULL;
 	gsize contents_len;
 	gs_free_error GError *error = NULL;
@@ -196,14 +180,14 @@ nm_key_file_db_start (NMKeyFileDB *self)
 
 	self->is_started = TRUE;
 
-	r = nm_utils_file_get_contents (-1,
-	                                self->filename,
-	                                20*1024*1024,
-	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                                &contents,
-	                                &contents_len,
-	                                &error);
-	if (r < 0) {
+	if (!nm_utils_file_get_contents (-1,
+	                                 self->filename,
+	                                 20*1024*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+	                                 &contents,
+	                                 &contents_len,
+	                                 NULL,
+	                                 &error)) {
 		_LOGD ("failed to read \"%s\": %s", self->filename, error->message);
 		return;
 	}
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h
index 8563f4d1..0f729c72 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.h
+++ b/shared/nm-glib-aux/nm-keyfile-aux.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_KEYFILE_AUX_H__
diff --git a/shared/nm-glib-aux/nm-logging-base.c b/shared/nm-glib-aux/nm-logging-base.c
new file mode 100644
index 00000000..47b3e993
--- /dev/null
+++ b/shared/nm-glib-aux/nm-logging-base.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include "nm-logging-base.h"
+
+#include <syslog.h>
+
+/*****************************************************************************/
+
+const LogLevelDesc level_desc[_LOGL_N] = {
+	[LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   },
+	[LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   },
+	[LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_INFO,    },
+	[LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_MESSAGE, },
+	[LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_MESSAGE, },
+	[_LOGL_OFF]  = { "OFF",   NULL,      0,           0,                   },
+	[_LOGL_KEEP] = { "KEEP",  NULL,      0,           0,                   },
+};
+
+gboolean
+_nm_log_parse_level (const char  *level,
+                     NMLogLevel  *out_level)
+{
+	int i;
+
+	if (!level)
+		return FALSE;
+
+	for (i = 0; i < (int) G_N_ELEMENTS (level_desc); i++) {
+		if (!g_ascii_strcasecmp (level_desc[i].name, level)) {
+			NM_SET_OUT (out_level, i);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
diff --git a/shared/nm-glib-aux/nm-logging-base.h b/shared/nm-glib-aux/nm-logging-base.h
new file mode 100644
index 00000000..3d964a6e
--- /dev/null
+++ b/shared/nm-glib-aux/nm-logging-base.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#ifndef __NM_LOGGING_BASE_H__
+#define __NM_LOGGING_BASE_H__
+
+#include "nm-logging-fwd.h"
+
+typedef struct {
+	const char *name;
+	const char *level_str;
+
+	/* nm-logging uses syslog internally. Note that the three most-verbose syslog levels
+	 * are LOG_DEBUG, LOG_INFO and LOG_NOTICE. Journal already highlights LOG_NOTICE
+	 * as special.
+	 *
+	 * On the other hand, we have three levels LOGL_TRACE, LOGL_DEBUG and LOGL_INFO,
+	 * which are regular messages not to be highlighted. For that reason, we must map
+	 * LOGL_TRACE and LOGL_DEBUG both to syslog level LOG_DEBUG. */
+	int syslog_level;
+
+	GLogLevelFlags g_log_level;
+} LogLevelDesc;
+
+extern const LogLevelDesc level_desc[_LOGL_N];
+
+gboolean _nm_log_parse_level (const char *level,
+                              NMLogLevel *out_level);
+
+#endif /* __NM_LOGGING_BASE_H__ */
diff --git a/shared/nm-glib-aux/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h
index c60a20b5..47661788 100644
--- a/shared/nm-glib-aux/nm-logging-fwd.h
+++ b/shared/nm-glib-aux/nm-logging-fwd.h
@@ -1,26 +1,11 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2006 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef __NM_LOGGING_DEFINES_H__
-#define __NM_LOGGING_DEFINES_H__
+#ifndef __NM_LOGGING_FWD_H__
+#define __NM_LOGGING_FWD_H__
 
 /* Log domains */
 
@@ -127,6 +112,19 @@ nm_log_level_from_syslog (int syslog_level)
 	}
 }
 
+static inline int
+nm_log_level_to_syslog (NMLogLevel nm_level)
+{
+	switch (nm_level) {
+	case LOGL_ERR:    return 3; /* LOG_ERR */
+	case LOGL_WARN:   return 4; /* LOG_WARN */
+	case LOGL_INFO:   return 5; /* LOG_NOTICE */
+	case LOGL_DEBUG:  return 6; /* LOG_INFO */
+	case LOGL_TRACE:  return 7; /* LOG_DEBUG */
+	default:          return 0; /* LOG_EMERG */
+	}
+}
+
 /*****************************************************************************/
 
 struct timespec;
@@ -137,4 +135,120 @@ extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp
                                                        gint64 offset_sec,
                                                        gboolean is_boottime);
 
-#endif /* __NM_LOGGING_DEFINES_H__ */
+/*****************************************************************************/
+
+#define _LOGL_TRACE LOGL_TRACE
+#define _LOGL_DEBUG LOGL_DEBUG
+#define _LOGL_INFO  LOGL_INFO
+#define _LOGL_WARN  LOGL_WARN
+#define _LOGL_ERR   LOGL_ERR
+
+/* This is the default definition of _NMLOG_ENABLED(). Special implementations
+ * might want to undef this and redefine it. */
+#define _NMLOG_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG_DOMAIN)) )
+
+#define _LOGT(...)          _NMLOG (_LOGL_TRACE, __VA_ARGS__)
+#define _LOGD(...)          _NMLOG (_LOGL_DEBUG, __VA_ARGS__)
+#define _LOGI(...)          _NMLOG (_LOGL_INFO , __VA_ARGS__)
+#define _LOGW(...)          _NMLOG (_LOGL_WARN , __VA_ARGS__)
+#define _LOGE(...)          _NMLOG (_LOGL_ERR  , __VA_ARGS__)
+
+#define _LOGT_ENABLED(...)  _NMLOG_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOGD_ENABLED(...)  _NMLOG_ENABLED (_LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOGI_ENABLED(...)  _NMLOG_ENABLED (_LOGL_INFO , ##__VA_ARGS__)
+#define _LOGW_ENABLED(...)  _NMLOG_ENABLED (_LOGL_WARN , ##__VA_ARGS__)
+#define _LOGE_ENABLED(...)  _NMLOG_ENABLED (_LOGL_ERR  , ##__VA_ARGS__)
+
+#define _LOGT_err(errsv, ...) _NMLOG_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#define _LOGD_err(errsv, ...) _NMLOG_err (errsv, _LOGL_DEBUG, __VA_ARGS__)
+#define _LOGI_err(errsv, ...) _NMLOG_err (errsv, _LOGL_INFO , __VA_ARGS__)
+#define _LOGW_err(errsv, ...) _NMLOG_err (errsv, _LOGL_WARN , __VA_ARGS__)
+#define _LOGE_err(errsv, ...) _NMLOG_err (errsv, _LOGL_ERR  , __VA_ARGS__)
+
+/* _LOGT() and _LOGt() both log with level TRACE, but the latter is disabled by default,
+ * unless building with --with-more-logging. */
+#if NM_MORE_LOGGING
+#define _LOGt_ENABLED(...)    _NMLOG_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOGt(...)            _NMLOG (_LOGL_TRACE, __VA_ARGS__)
+#define _LOGt_err(errsv, ...) _NMLOG_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimized out. */
+#define _LOGt_ENABLED(...)    ( FALSE && (_NMLOG_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOGt(...)            G_STMT_START { if (FALSE) { _NMLOG (_LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#define _LOGt_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG_err (errsv, _LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+/*****************************************************************************/
+
+/* Some implementation define a second set of logging macros, for a separate
+ * use. As with the _LOGD() macro family above, the exact implementation
+ * depends on the file that uses them.
+ * Still, it encourages a common pattern to have the common set of macros
+ * like _LOG2D(), _LOG2I(), etc. and have _LOG2t() which by default
+ * is disabled at compile time. */
+
+#define _NMLOG2_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG2_DOMAIN)) )
+
+#define _LOG2T(...)          _NMLOG2 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG2D(...)          _NMLOG2 (_LOGL_DEBUG, __VA_ARGS__)
+#define _LOG2I(...)          _NMLOG2 (_LOGL_INFO , __VA_ARGS__)
+#define _LOG2W(...)          _NMLOG2 (_LOGL_WARN , __VA_ARGS__)
+#define _LOG2E(...)          _NMLOG2 (_LOGL_ERR  , __VA_ARGS__)
+
+#define _LOG2T_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG2D_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOG2I_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_INFO , ##__VA_ARGS__)
+#define _LOG2W_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_WARN , ##__VA_ARGS__)
+#define _LOG2E_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_ERR  , ##__VA_ARGS__)
+
+#define _LOG2T_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#define _LOG2D_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_DEBUG, __VA_ARGS__)
+#define _LOG2I_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_INFO , __VA_ARGS__)
+#define _LOG2W_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_WARN , __VA_ARGS__)
+#define _LOG2E_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_ERR  , __VA_ARGS__)
+
+#if NM_MORE_LOGGING
+#define _LOG2t_ENABLED(...)    _NMLOG2_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG2t(...)            _NMLOG2 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG2t_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimized out. */
+#define _LOG2t_ENABLED(...)    ( FALSE && (_NMLOG2_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOG2t(...)            G_STMT_START { if (FALSE) { _NMLOG2 (_LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#define _LOG2t_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG2_err (errsv, _LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+#define _NMLOG3_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG3_DOMAIN)) )
+
+#define _LOG3T(...)          _NMLOG3 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG3D(...)          _NMLOG3 (_LOGL_DEBUG, __VA_ARGS__)
+#define _LOG3I(...)          _NMLOG3 (_LOGL_INFO , __VA_ARGS__)
+#define _LOG3W(...)          _NMLOG3 (_LOGL_WARN , __VA_ARGS__)
+#define _LOG3E(...)          _NMLOG3 (_LOGL_ERR  , __VA_ARGS__)
+
+#define _LOG3T_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG3D_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOG3I_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_INFO , ##__VA_ARGS__)
+#define _LOG3W_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_WARN , ##__VA_ARGS__)
+#define _LOG3E_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_ERR  , ##__VA_ARGS__)
+
+#define _LOG3T_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#define _LOG3D_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_DEBUG, __VA_ARGS__)
+#define _LOG3I_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_INFO , __VA_ARGS__)
+#define _LOG3W_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_WARN , __VA_ARGS__)
+#define _LOG3E_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_ERR  , __VA_ARGS__)
+
+#if NM_MORE_LOGGING
+#define _LOG3t_ENABLED(...)    _NMLOG3_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG3t(...)            _NMLOG3 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG3t_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimized out. */
+#define _LOG3t_ENABLED(...)    ( FALSE && (_NMLOG3_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOG3t(...)            G_STMT_START { if (FALSE) { _NMLOG3 (_LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#define _LOG3t_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG3_err (errsv, _LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+/*****************************************************************************/
+
+#endif /* __NM_LOGGING_FWD_H__ */
diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h
index 9502c442..d1ea04c0 100644
--- a/shared/nm-glib-aux/nm-macros-internal.h
+++ b/shared/nm-glib-aux/nm-macros-internal.h
@@ -1,22 +1,7 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2012 Colin Walters <walters@verbum.org>.
- * (C) Copyright 2014 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2012 Colin Walters <walters@verbum.org>.
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NM_MACROS_INTERNAL_H__
@@ -324,6 +309,12 @@ _nm_auto_protect_errno (int *p_saved_errno)
 NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref);
 #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
 
+NM_AUTO_DEFINE_FCN0 (guint, _nm_auto_remove_source, g_source_remove);
+#define nm_auto_remove_source nm_auto(_nm_auto_remove_source)
+
+NM_AUTO_DEFINE_FCN0 (GIOChannel *, _nm_auto_unref_io_channel, g_io_channel_unref)
+#define nm_auto_unref_io_channel nm_auto(_nm_auto_unref_io_channel)
+
 NM_AUTO_DEFINE_FCN0 (GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref);
 #define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop)
 
@@ -644,6 +635,13 @@ NM_G_ERROR_MSG (GError *error)
 #define _NM_ENSURE_TYPE_CONST(type, value) ((const type) (value))
 #endif
 
+#if _NM_CC_SUPPORT_GENERIC && ( !defined (__clang__) || __clang_major__ > 3 )
+#define NM_STRUCT_OFFSET_ENSURE_TYPE(type, container, field) (_Generic ( (&(((container *) NULL)->field))[0] , \
+                                                                        type: G_STRUCT_OFFSET (container, field)))
+#else
+#define NM_STRUCT_OFFSET_ENSURE_TYPE(type, container, field) G_STRUCT_OFFSET (container, field)
+#endif
+
 #if _NM_CC_SUPPORT_GENERIC
 /* these macros cast (value) to
  *  - "const char **"      (for "MC", mutable-const)
@@ -1045,7 +1043,7 @@ _nm_gobject_notify_together_impl (obj_type *obj, guint n, const property_enums_t
 		g_object_thaw_notify ((GObject *) obj); \
 } \
 \
-static inline void \
+_nm_unused static inline void \
 _notify (obj_type *obj, property_enums_type prop) \
 { \
 	_nm_gobject_notify_together_impl (obj, 1, &prop); \
@@ -1270,6 +1268,25 @@ nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellabl
 
 /*****************************************************************************/
 
+static inline const char *
+nm_dbus_path_not_empty (const char *str)
+{
+	nm_assert (!str || str[0] == '/');
+	return !str || (str[0] == '/' && str[1] == '\0')
+	       ? NULL
+	       : str;
+}
+
+/*****************************************************************************/
+
+/* GVariantType is basically a C string. But G_VARIANT_TYPE() is not suitable
+ * to initialize a static variable (because it evaluates a function check that
+ * the string is valid). Add an alternative macro that does the plain cast.
+ *
+ * Here you loose the assertion check that G_VARIANT_TYPE() to ensure the
+ * string is valid. */
+#define NM_G_VARIANT_TYPE(fmt) ((const GVariantType *) (""fmt""))
+
 static inline GVariant *
 nm_g_variant_ref (GVariant *v)
 {
@@ -1989,4 +2006,18 @@ nm_close (int fd)
 
 #define NM_PID_T_INVAL ((pid_t) -1)
 
+/*****************************************************************************/
+
+NM_AUTO_DEFINE_FCN_VOID0 (GMutex *, _nm_auto_unlock_g_mutex, g_mutex_unlock)
+
+#define nm_auto_unlock_g_mutex nm_auto (_nm_auto_unlock_g_mutex)
+
+#define _NM_G_MUTEX_LOCKED(lock, uniq) \
+	nm_auto_unlock_g_mutex GMutex *NM_UNIQ_T(nm_lock, uniq) = (lock)
+
+#define NM_G_MUTEX_LOCKED(lock) \
+	_NM_G_MUTEX_LOCKED (lock, NM_UNIQ)
+
+/*****************************************************************************/
+
 #endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h
index 06016bdd..cbfdba63 100644
--- a/shared/nm-glib-aux/nm-obj.h
+++ b/shared/nm-glib-aux/nm-obj.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_OBJ_H__
diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c
index f56f8b99..95063c94 100644
--- a/shared/nm-glib-aux/nm-random-utils.c
+++ b/shared/nm-glib-aux/nm-random-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h
index 8e134ee9..056af38a 100644
--- a/shared/nm-glib-aux/nm-random-utils.h
+++ b/shared/nm-glib-aux/nm-random-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_RANDOM_UTILS_H__
diff --git a/shared/nm-glib-aux/nm-ref-string.c b/shared/nm-glib-aux/nm-ref-string.c
new file mode 100644
index 00000000..0a0b0d3a
--- /dev/null
+++ b/shared/nm-glib-aux/nm-ref-string.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include "nm-ref-string.h"
+
+/*****************************************************************************/
+
+typedef struct {
+	NMRefString r;
+	volatile int ref_count;
+	char str_data[];
+} RefString;
+
+G_LOCK_DEFINE_STATIC (gl_lock);
+static GHashTable *gl_hash;
+
+/* the first field of NMRefString is a pointer to the NUL terminated string.
+ * This also allows to compare strings with nm_pstr_equal(), although, pointer
+ * equality might be better. */
+G_STATIC_ASSERT (G_STRUCT_OFFSET (NMRefString, str) == 0);
+G_STATIC_ASSERT (G_STRUCT_OFFSET (RefString, r) == 0);
+G_STATIC_ASSERT (G_STRUCT_OFFSET (RefString, r.str) == 0);
+
+/*****************************************************************************/
+
+static guint
+_ref_string_hash (gconstpointer ptr)
+{
+	const RefString *a = ptr;
+	NMHashState h;
+
+	nm_hash_init (&h, 1463435489u);
+	nm_hash_update (&h, a->r.str, a->r.len);
+	return nm_hash_complete (&h);
+}
+
+static gboolean
+_ref_string_equal (gconstpointer pa, gconstpointer pb)
+{
+	const RefString *a = pa;
+	const RefString *b = pb;
+
+	return    a->r.len == b->r.len
+	       && memcmp (a->r.str, b->r.str, a->r.len) == 0;
+}
+
+/*****************************************************************************/
+
+static void
+_ASSERT (const RefString *rstr0)
+{
+#if NM_MORE_ASSERTS
+	int r;
+
+	nm_assert (rstr0);
+
+	G_LOCK (gl_lock);
+	r = g_atomic_int_get (&rstr0->ref_count);
+
+	nm_assert (r > 0);
+	nm_assert (r < G_MAXINT);
+
+	nm_assert (rstr0 == g_hash_table_lookup (gl_hash, rstr0));
+	G_UNLOCK (gl_lock);
+#endif
+}
+
+/**
+ * nm_ref_string_new_len:
+ * @cstr: the string to intern. Must contain @len bytes.
+ *   If @len is zero, @cstr may be %NULL. Note that it is
+ *   accetable that the string contains a NUL character
+ *   within the first @len bytes. That is, the string is
+ *   not treated as a NUL terminated string, but as binary.
+ *   Also, contrary to strncpy(), this will read all the
+ *   first @len bytes. It won't stop at the first NUL.
+ * @len: the length of the string (usually there is no NUL character
+ *   within the first @len bytes, but that would be acceptable as well
+ *   to add binary data).
+ *
+ * Note that the resulting NMRefString instance will always be NUL terminated
+ * (at position @len).
+ *
+ * Note that NMRefString are always interned/deduplicated. If such a string
+ * already exists, the existing instance will be refered and returned.
+ *
+ *
+ * Since all NMRefString are shared and interned, you may use
+ * pointer equality to compare them. Note that if a NMRefString contains
+ * a NUL character (meaning, if
+ *
+ *    strlen (nm_ref_string_get_str (str)) != nm_ref_string_get_len (str)
+ *
+ * ), then pointer in-equality does not mean that the NUL terminated strings
+ * are also unequal. In other words, for strings that contain NUL characters,
+ *
+ *    if (str1 != str2)
+ *       assert (!nm_streq0 (nm_ref_string_get_str (str1), nm_ref_string_get_str (str2)));
+ *
+ * might not hold!
+ *
+ *
+ * NMRefString is thread-safe.
+ *
+ * Returns: (transfer full): the interned string. This is
+ *   never %NULL, but note that %NULL is also a valid NMRefString.
+ *   The result must be unrefed with nm_ref_string_unref().
+ */
+NMRefString *
+nm_ref_string_new_len (const char *cstr, gsize len)
+{
+	RefString *rstr0;
+
+	G_LOCK (gl_lock);
+
+	if (G_UNLIKELY (!gl_hash)) {
+		gl_hash = g_hash_table_new_full (_ref_string_hash, _ref_string_equal, g_free, NULL);
+		rstr0 = NULL;
+	} else {
+		NMRefString rr_lookup = {
+			.len = len,
+			.str = cstr,
+		};
+
+		rstr0 = g_hash_table_lookup (gl_hash, &rr_lookup);
+	}
+
+	if (rstr0) {
+		nm_assert (({
+		               int r = g_atomic_int_get (&rstr0->ref_count);
+
+		               (r >= 0 && r < G_MAXINT);
+		            }));
+		g_atomic_int_inc (&rstr0->ref_count);
+	} else {
+		rstr0 = g_malloc (sizeof (RefString) + 1 + len);
+		rstr0->ref_count = 1;
+		*((gsize *) &rstr0->r.len) = len;
+		*((const char **) &rstr0->r.str) = rstr0->str_data;
+		if (len > 0)
+			memcpy (rstr0->str_data, cstr, len);
+		rstr0->str_data[len] = '\0';
+
+		if (!g_hash_table_add (gl_hash, rstr0))
+			nm_assert_not_reached ();
+	}
+
+	G_UNLOCK (gl_lock);
+
+	return &rstr0->r;
+}
+
+NMRefString *
+nm_ref_string_ref (NMRefString *rstr)
+{
+	RefString *const rstr0 = (RefString *) rstr;
+
+	if (!rstr)
+		return NULL;
+
+	_ASSERT (rstr0);
+
+	g_atomic_int_inc (&rstr0->ref_count);
+	return &rstr0->r;
+}
+
+void
+_nm_ref_string_unref_non_null (NMRefString *rstr)
+{
+	RefString *const rstr0 = (RefString *) rstr;
+
+	_ASSERT (rstr0);
+
+	if (G_LIKELY (!g_atomic_int_dec_and_test (&rstr0->ref_count)))
+		return;
+
+	G_LOCK (gl_lock);
+
+	/* in the fast-path above, we already decremented the ref-count to zero.
+	 * We need recheck that the ref-count is still zero. */
+
+	if (g_atomic_int_get (&rstr0->ref_count) == 0) {
+		if (!g_hash_table_remove (gl_hash, rstr0))
+			nm_assert_not_reached ();
+	} else {
+#if NM_MORE_ASSERTS > 5
+		nm_assert (g_hash_table_lookup (gl_hash, rstr0) == rstr0);
+#endif
+	}
+
+	G_UNLOCK (gl_lock);
+}
+
+/*****************************************************************************/
diff --git a/shared/nm-glib-aux/nm-ref-string.h b/shared/nm-glib-aux/nm-ref-string.h
new file mode 100644
index 00000000..c80b75ea
--- /dev/null
+++ b/shared/nm-glib-aux/nm-ref-string.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#ifndef __NM_REF_STRING_H__
+#define __NM_REF_STRING_H__
+
+/*****************************************************************************/
+
+typedef struct _NMRefString {
+	const char *const str;
+	const gsize len;
+} NMRefString;
+
+/*****************************************************************************/
+
+NMRefString *nm_ref_string_new_len (const char *cstr, gsize len);
+
+static inline NMRefString *
+nm_ref_string_new (const char *cstr)
+{
+	return   cstr
+	       ? nm_ref_string_new_len (cstr, strlen (cstr))
+	       : NULL;
+}
+
+NMRefString *nm_ref_string_ref (NMRefString *rstr);
+void _nm_ref_string_unref_non_null (NMRefString *rstr);
+
+static inline void
+nm_ref_string_unref (NMRefString *rstr)
+{
+	if (rstr)
+		_nm_ref_string_unref_non_null (rstr);
+}
+
+NM_AUTO_DEFINE_FCN_VOID0 (NMRefString *, _nm_auto_ref_string, _nm_ref_string_unref_non_null)
+#define nm_auto_ref_string nm_auto(_nm_auto_ref_string)
+
+/*****************************************************************************/
+
+static inline const char *
+nm_ref_string_get_str (NMRefString *rstr)
+{
+	return rstr ? rstr->str : NULL;
+}
+
+static inline gsize
+nm_ref_string_get_len (NMRefString *rstr)
+{
+	return rstr ? rstr->len : 0u;
+}
+
+static inline gboolean
+NM_IS_REF_STRING (const NMRefString *rstr)
+{
+#if NM_MORE_ASSERTS > 10
+	if (rstr) {
+		nm_auto_ref_string NMRefString *r2 = NULL;
+
+		r2 = nm_ref_string_new_len (rstr->str, rstr->len);
+		nm_assert (rstr == r2);
+	}
+#endif
+
+	/* Technically, %NULL is also a valid NMRefString (according to nm_ref_string_new(),
+	 * nm_ref_string_get_str() and nm_ref_string_unref()). However, NM_IS_REF_STRING()
+	 * does not think so. If callers want to allow %NULL, they need to check
+	 * separately. */
+	return !!rstr;
+}
+
+#endif /* __NM_REF_STRING_H__ */
diff --git a/shared/nm-glib-aux/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c
index aeb88877..5b0afe46 100644
--- a/shared/nm-glib-aux/nm-secret-utils.c
+++ b/shared/nm-glib-aux/nm-secret-utils.c
@@ -1,22 +1,7 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
- * (C) Copyright 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ * Copyright (C) 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h
index 0fd1ac8b..1b98b7e9 100644
--- a/shared/nm-glib-aux/nm-secret-utils.h
+++ b/shared/nm-glib-aux/nm-secret-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_SECRET_UTILS_H__
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index 9bfd5ae2..8e1c8b58 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -26,6 +11,7 @@
 #include <poll.h>
 #include <fcntl.h>
 #include <sys/syscall.h>
+#include <glib-unix.h>
 
 #include "nm-errno.h"
 
@@ -555,6 +541,102 @@ _nm_utils_ip4_prefix_to_netmask (guint32 prefix)
 	return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
 }
 
+gconstpointer
+nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen)
+{
+	g_return_val_if_fail (dst, NULL);
+
+	switch (family) {
+	case AF_INET:
+		g_return_val_if_fail (plen <= 32, NULL);
+
+		if (!src) {
+			/* allow "self-assignment", by specifying %NULL as source. */
+			src = dst;
+		}
+
+		*((guint32 *) dst) = nm_utils_ip4_address_clear_host_address (*((guint32 *) src), plen);
+		break;
+	case AF_INET6:
+		nm_utils_ip6_address_clear_host_address (dst, src, plen);
+		break;
+	default:
+		g_return_val_if_reached (NULL);
+	}
+	return dst;
+}
+
+/* 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 or set @src NULL.
+ */
+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 (dst, NULL);
+
+	if (!src)
+		src = dst;
+
+	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;
+}
+
+int
+nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen)
+{
+	int nbytes;
+	guint8 va, vb, m;
+
+	if (plen >= 128)
+		NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, sizeof (struct in6_addr));
+	else {
+		nbytes = plen / 8;
+		if (nbytes)
+			NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, nbytes);
+
+		plen = plen % 8;
+		if (plen != 0) {
+			m = ~((1 << (8 - plen)) - 1);
+			va = ((((const guint8 *) addr_a))[nbytes]) & m;
+			vb = ((((const guint8 *) addr_b))[nbytes]) & m;
+			NM_CMP_DIRECT (va, vb);
+		}
+	}
+	return 0;
+}
+
 /**
  * _nm_utils_ip4_get_default_prefix:
  * @ip: an IPv4 address (in network byte order)
@@ -601,11 +683,80 @@ nm_utils_ip_is_site_local (int addr_family,
 
 /*****************************************************************************/
 
+static gboolean
+_parse_legacy_addr4 (const char *text, in_addr_t *out_addr)
+{
+	gs_free char *s_free = NULL;
+	struct in_addr a1;
+	guint8 bin[sizeof (a1)];
+	char *s;
+	int i;
+
+	if (inet_aton (text, &a1) != 1)
+		return FALSE;
+
+	/* OK, inet_aton() accepted the format. That's good, because we want
+	 * to accept IPv4 addresses in octal format, like 255.255.000.000.
+	 * That's what "legacy" means here. inet_pton() doesn't accept those.
+	 *
+	 * But inet_aton() also ignores trailing garbage and formats with fewer than
+	 * 4 digits. That is just too crazy and we don't do that. Perform additional checks
+	 * and reject some forms that inet_aton() accepted.
+	 *
+	 * Note that we still should (of course) accept everything that inet_pton()
+	 * accepts. However this code never gets called if inet_pton() succeeds
+	 * (see below, aside the assertion code). */
+
+	if (NM_STRCHAR_ANY (text, ch, (   !(ch >= '0' && ch <= '9')
+	                               && !NM_IN_SET (ch, '.', 'x')))) {
+		/* We only accepts '.', digits, and 'x' for "0x". */
+		return FALSE;
+	}
+
+	s = nm_memdup_maybe_a (300, text, strlen (text) + 1, &s_free);
+
+	for (i = 0; i < G_N_ELEMENTS (bin); i++) {
+		char *current_token = s;
+		gint32 v;
+
+		s = strchr (s, '.');
+		if (s) {
+			s[0] = '\0';
+			s++;
+		}
+
+		if ((i == G_N_ELEMENTS (bin) - 1) != (s == NULL)) {
+			/* Exactly for the last digit, we expect to have no more following token.
+			 * But this isn't the case. Abort. */
+			return FALSE;
+		}
+
+		v = _nm_utils_ascii_str_to_int64 (current_token, 0, 0, 0xFF, -1);
+		if (v == -1) {
+			/* we do accept octal and hex (even with leading "0x"). But something
+			 * about this token is wrong. */
+			return FALSE;
+		}
+
+		bin[i] = v;
+	}
+
+	if (memcmp (bin, &a1, sizeof (bin)) != 0) {
+		/* our parsing did not agree with what inet_aton() gave. Something
+		 * is wrong. Abort. */
+		return FALSE;
+	}
+
+	*out_addr = a1.s_addr;
+	return TRUE;
+}
+
 gboolean
-nm_utils_parse_inaddr_bin (int addr_family,
-                           const char *text,
-                           int *out_addr_family,
-                           gpointer out_addr)
+nm_utils_parse_inaddr_bin_full (int addr_family,
+                                gboolean accept_legacy,
+                                const char *text,
+                                int *out_addr_family,
+                                gpointer out_addr)
 {
 	NMIPAddr addrbin;
 
@@ -617,8 +768,26 @@ nm_utils_parse_inaddr_bin (int addr_family,
 	} else
 		g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
 
-	if (inet_pton (addr_family, text, &addrbin) != 1)
-		return FALSE;
+	if (inet_pton (addr_family, text, &addrbin) != 1) {
+		if (   accept_legacy
+		    && addr_family == AF_INET
+		    && _parse_legacy_addr4 (text, &addrbin.addr4)) {
+			/* The address is in some legacy format which inet_aton() accepts, but not inet_pton().
+			 * Most likely octal digits (leading zeros). We accept the address. */
+		} else
+			return FALSE;
+	}
+
+#if NM_MORE_ASSERTS > 10
+	if (addr_family == AF_INET) {
+		in_addr_t a;
+
+		/* The legacy parser should accept everything that inet_pton() accepts too. Meaning,
+		 * it should strictly parse *more* formats. And of course, parse it the same way. */
+		nm_assert (_parse_legacy_addr4 (text, &a));
+		nm_assert (addrbin.addr4 == a);
+	}
+#endif
 
 	NM_SET_OUT (out_addr_family, addr_family);
 	if (out_addr)
@@ -2302,6 +2471,44 @@ nm_utils_hash_keys_to_array (GHashTable *hash,
 	return keys;
 }
 
+gpointer *
+nm_utils_hash_values_to_array (GHashTable *hash,
+                               GCompareDataFunc compare_func,
+                               gpointer user_data,
+                               guint *out_len)
+{
+	GHashTableIter iter;
+	gpointer value;
+	gpointer *arr;
+	guint i, len;
+
+	if (   !hash
+	    || (len = g_hash_table_size (hash)) == 0u) {
+		NM_SET_OUT (out_len, 0);
+		return NULL;
+	}
+
+	arr = g_new (gpointer, ((gsize) len) + 1);
+	i = 0;
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &value))
+		arr[i++] = value;
+
+	nm_assert (i == len);
+	arr[len] = NULL;
+
+	if (   len > 1
+	    && compare_func) {
+		g_qsort_with_data (arr,
+		                   len,
+		                   sizeof (gpointer),
+		                   compare_func,
+		                   user_data);
+	}
+
+	return arr;
+}
+
 gboolean
 nm_utils_hashtable_same_keys (const GHashTable *a,
                               const GHashTable *b)
@@ -2370,6 +2577,10 @@ nm_utils_strv_make_deep_copied_n (const char **strv, gsize len)
  *   is negative or zero (in which case %NULL will be returned).
  * @len: the length of strings in @str. If negative, strv is assumed
  *   to be a NULL terminated array.
+ * @deep_copied: if %TRUE, clones the individual strings. In that case,
+ *   the returned array must be freed with g_strfreev(). Otherwise, the
+ *   strings themself are not copied. You must take care of who owns the
+ *   strings yourself.
  *
  * Like g_strdupv(), with two differences:
  *
@@ -2384,10 +2595,13 @@ nm_utils_strv_make_deep_copied_n (const char **strv, gsize len)
  * array with g_strfreev(). Allowing that would be error prone.
  *
  * Returns: (transfer full): a clone of the strv array. Always
- *   %NULL terminated.
+ *   %NULL terminated. Depending on @deep_copied, the strings are
+ *   cloned or not.
  */
 char **
-nm_utils_strv_dup (gpointer strv, gssize len)
+nm_utils_strv_dup (gpointer strv,
+                   gssize len,
+                   gboolean deep_copied)
 {
 	gsize i, l;
 	char **v;
@@ -2415,7 +2629,10 @@ nm_utils_strv_dup (gpointer strv, gssize len)
 			g_return_val_if_reached (v);
 		}
 
-		v[i] = g_strdup (src[i]);
+		if (deep_copied)
+			v[i] = g_strdup (src[i]);
+		else
+			v[i] = (char *) src[i];
 	}
 	v[l] = NULL;
 	return v;
@@ -2657,7 +2874,9 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 
 	g_return_val_if_fail (pid > 0, 0);
 
-	nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+	G_STATIC_ASSERT_EXPR (sizeof (GPid) >= sizeof (pid_t));
+
+	nm_sprintf_buf (filename, "/proc/%"G_PID_FORMAT"/stat", (GPid) pid);
 
 	if (!g_file_get_contents (filename, &contents, &length, NULL))
 		goto fail;
@@ -2845,6 +3064,24 @@ nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b)
 	}
 }
 
+char *
+nm_utils_g_slist_strlist_join (const GSList *a, const char *separator)
+{
+	GString *str = NULL;
+
+	if (!a)
+		return NULL;
+
+	for (; a; a = a->next) {
+		if (!str)
+			str = g_string_new (NULL);
+		else
+			g_string_append (str, separator);
+		g_string_append (str, a->data);
+	}
+	return g_string_free (str, FALSE);
+}
+
 /*****************************************************************************/
 
 gpointer
@@ -3275,3 +3512,478 @@ nm_utils_gvariant_vardict_filter_drop_one (GVariant *src,
 	                                         _gvariant_vardict_filter_drop_one,
 	                                         (gpointer) key);
 }
+
+/*****************************************************************************/
+
+static gboolean
+debug_key_matches (const char *key,
+                   const char *token,
+                   guint        length)
+{
+	/* may not call GLib functions: see note in g_parse_debug_string() */
+	for (; length; length--, key++, token++) {
+		char k = (*key   == '_') ? '-' : g_ascii_tolower (*key  );
+		char t = (*token == '_') ? '-' : g_ascii_tolower (*token);
+
+		if (k != t)
+			return FALSE;
+	}
+
+	return *key == '\0';
+}
+
+/**
+ * nm_utils_parse_debug_string:
+ * @string: the string to parse
+ * @keys: the debug keys
+ * @nkeys: number of entries in @keys
+ *
+ * Similar to g_parse_debug_string(), but does not special
+ * case "help" or "all".
+ *
+ * Returns: the flags
+ */
+guint
+nm_utils_parse_debug_string (const char *string,
+                             const GDebugKey *keys,
+                             guint nkeys)
+{
+	guint i;
+	guint result = 0;
+	const char *q;
+
+	if (string == NULL)
+		return 0;
+
+	while (*string) {
+		q = strpbrk (string, ":;, \t");
+		if (!q)
+			q = string + strlen (string);
+
+		for (i = 0; i < nkeys; i++) {
+			if (debug_key_matches (keys[i].key, string, q - string))
+				result |= keys[i].value;
+		}
+
+		string = q;
+		if (*string)
+			string++;
+	}
+
+	return result;
+}
+
+/*****************************************************************************/
+
+GSource *
+nm_g_idle_source_new (int priority,
+                      GSourceFunc func,
+                      gpointer user_data,
+                      GDestroyNotify destroy_notify)
+{
+	GSource *source;
+
+	source = g_idle_source_new ();
+	if (priority != G_PRIORITY_DEFAULT)
+		g_source_set_priority (source, priority);
+	g_source_set_callback (source, func, user_data, destroy_notify);
+	return source;
+}
+
+GSource *
+nm_g_timeout_source_new (guint timeout_ms,
+                         int priority,
+                         GSourceFunc func,
+                         gpointer user_data,
+                         GDestroyNotify destroy_notify)
+{
+	GSource *source;
+
+	source = g_timeout_source_new (timeout_ms);
+	if (priority != G_PRIORITY_DEFAULT)
+		g_source_set_priority (source, priority);
+	g_source_set_callback (source, func, user_data, destroy_notify);
+	return source;
+}
+
+GSource *
+nm_g_unix_signal_source_new (int signum,
+                             int priority,
+                             GSourceFunc handler,
+                             gpointer user_data,
+                             GDestroyNotify notify)
+{
+	GSource *source;
+
+	source = g_unix_signal_source_new (signum);
+
+	if (priority != G_PRIORITY_DEFAULT)
+		g_source_set_priority (source, priority);
+	g_source_set_callback (source, handler, user_data, notify);
+	return source;
+}
+
+/*****************************************************************************/
+
+#define _CTX_LOG(fmt, ...) \
+	G_STMT_START { \
+		if (FALSE) { \
+			gint64 _ts = g_get_monotonic_time () / 100; \
+			\
+			g_printerr (">>>> [%"G_GINT64_FORMAT".%05"G_GINT64_FORMAT"] [src:%p]: " fmt "\n", \
+			            _ts / 10000, \
+			            _ts % 10000, \
+			            (ctx_src), \
+			            ##__VA_ARGS__); \
+		} \
+	} G_STMT_END
+
+typedef struct {
+	int fd;
+	guint events;
+	guint registered_events;
+	union {
+		int one;
+		int *many;
+	} idx;
+	gpointer tag;
+	bool stale:1;
+	bool has_many_idx:1;
+} PollData;
+
+typedef struct {
+	GSource source;
+	GMainContext *context;
+	GHashTable *fds;
+	GPollFD *fds_arr;
+	int fds_len;
+	int max_priority;
+	bool acquired:1;
+} CtxIntegSource;
+
+static void
+_poll_data_free (gpointer user_data)
+{
+	PollData *poll_data = user_data;
+
+	if (poll_data->has_many_idx)
+		g_free (poll_data->idx.many);
+	nm_g_slice_free (poll_data);
+}
+
+static void
+_ctx_integ_source_reacquire (CtxIntegSource *ctx_src)
+{
+	if (G_LIKELY (   ctx_src->acquired
+	              && g_main_context_is_owner (ctx_src->context)))
+		return;
+
+	/* the parent context now iterates on a different thread.
+	 * We need to release and reacquire the inner context. */
+
+	if (ctx_src->acquired)
+		g_main_context_release (ctx_src->context);
+
+	if (G_UNLIKELY (!g_main_context_acquire (ctx_src->context))) {
+		/* Nobody is supposed to reacquire the context while we use it. This is a bug
+		 * of the user. */
+		ctx_src->acquired = FALSE;
+		g_return_if_reached ();
+	}
+	ctx_src->acquired = TRUE;
+}
+
+static gboolean
+_ctx_integ_source_prepare (GSource *source,
+                           int *out_timeout)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+	int max_priority;
+	int timeout = -1;
+	gboolean any_ready;
+	int fds_allocated;
+	int fds_len_old;
+	gs_free GPollFD *fds_arr_old = NULL;
+	GHashTableIter h_iter;
+	PollData *poll_data;
+	gboolean fds_changed;
+	int i;
+
+	_CTX_LOG ("prepare...");
+
+	_ctx_integ_source_reacquire (ctx_src);
+
+	any_ready = g_main_context_prepare (ctx_src->context, &max_priority);
+
+	fds_arr_old = g_steal_pointer (&ctx_src->fds_arr);
+	fds_len_old = ctx_src->fds_len;
+
+	fds_allocated = NM_MAX (1, fds_len_old); /* there is at least the wakeup's FD */
+	ctx_src->fds_arr = g_new (GPollFD, fds_allocated);
+
+	while ((ctx_src->fds_len = g_main_context_query (ctx_src->context,
+	                                                 max_priority,
+	                                                 &timeout,
+	                                                 ctx_src->fds_arr,
+	                                                 fds_allocated)) > fds_allocated) {
+		fds_allocated = ctx_src->fds_len;
+		g_free (ctx_src->fds_arr);
+		ctx_src->fds_arr = g_new (GPollFD, fds_allocated);
+	}
+
+	fds_changed = FALSE;
+	if (fds_len_old != ctx_src->fds_len)
+		fds_changed = TRUE;
+	else {
+		for (i = 0; i < ctx_src->fds_len; i++) {
+			if (   fds_arr_old[i].fd != ctx_src->fds_arr[i].fd
+			    || fds_arr_old[i].events != ctx_src->fds_arr[i].events) {
+				fds_changed = TRUE;
+				break;
+			}
+		}
+	}
+
+	if (G_UNLIKELY (fds_changed)) {
+
+		g_hash_table_iter_init (&h_iter, ctx_src->fds);
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL))
+			poll_data->stale = TRUE;
+
+		for (i = 0; i < ctx_src->fds_len; i++) {
+			const GPollFD *fd = &ctx_src->fds_arr[i];
+
+			poll_data = g_hash_table_lookup (ctx_src->fds, &fd->fd);
+
+			if (G_UNLIKELY (!poll_data)) {
+				poll_data = g_slice_new (PollData);
+				*poll_data = (PollData) {
+					.fd                = fd->fd,
+					.idx.one           = i,
+					.has_many_idx      = FALSE,
+					.events            = fd->events,
+					.registered_events = 0,
+					.tag               = NULL,
+					.stale             = FALSE,
+				};
+				g_hash_table_add (ctx_src->fds, poll_data);
+				nm_assert (poll_data == g_hash_table_lookup (ctx_src->fds, &fd->fd));
+				continue;
+			}
+
+			if (G_LIKELY (poll_data->stale)) {
+				if (poll_data->has_many_idx) {
+					g_free (poll_data->idx.many);
+					poll_data->has_many_idx = FALSE;
+				}
+				poll_data->events = fd->events;
+				poll_data->idx.one = i;
+				poll_data->stale = FALSE;
+				continue;
+			}
+
+			/* How odd. We have duplicate FDs. In fact, currently g_main_context_query() always
+			 * coalesces the FDs and this cannot happen. However, that is not documented behavior,
+			 * so we should not rely on that. So we need to keep a list of indexes... */
+			poll_data->events |= fd->events;
+			if (!poll_data->has_many_idx) {
+				int idx0;
+
+				idx0 = poll_data->idx.one;
+				poll_data->has_many_idx = TRUE;
+				poll_data->idx.many = g_new (int, 4);
+				poll_data->idx.many[0] = 2; /* number allocated */
+				poll_data->idx.many[1] = 2; /* number used */
+				poll_data->idx.many[2] = idx0;
+				poll_data->idx.many[3] = i;
+			} else {
+				if (poll_data->idx.many[0] == poll_data->idx.many[1]) {
+					poll_data->idx.many[0] *= 2;
+					poll_data->idx.many = g_realloc (poll_data->idx.many, sizeof (int) * (2 + poll_data->idx.many[0]));
+				}
+				poll_data->idx.many[2 + poll_data->idx.many[1]] = i;
+				poll_data->idx.many[1]++;
+			}
+
+		}
+
+		g_hash_table_iter_init (&h_iter, ctx_src->fds);
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) {
+			if (poll_data->stale) {
+				nm_assert (poll_data->tag);
+				nm_assert (poll_data->events == poll_data->registered_events);
+				_CTX_LOG ("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+				g_source_remove_unix_fd (&ctx_src->source, poll_data->tag);
+				g_hash_table_iter_remove (&h_iter);
+				continue;
+			}
+			if (!poll_data->tag) {
+				_CTX_LOG ("prepare: add poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+				poll_data->registered_events = poll_data->events;
+				poll_data->tag = g_source_add_unix_fd (&ctx_src->source, poll_data->fd, poll_data->registered_events);
+				continue;
+			}
+			if (poll_data->registered_events != poll_data->events) {
+				_CTX_LOG ("prepare: update poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+				poll_data->registered_events = poll_data->events;
+				g_source_modify_unix_fd (&ctx_src->source, poll_data->tag, poll_data->registered_events);
+			}
+		}
+	}
+
+	NM_SET_OUT (out_timeout, timeout);
+	ctx_src->max_priority = max_priority;
+
+	_CTX_LOG ("prepare: done, any-ready=%d, timeout=%d, max-priority=%d", any_ready, timeout, max_priority);
+
+	/* we always need to poll, because we have some file descriptors. */
+	return FALSE;
+}
+
+static gboolean
+_ctx_integ_source_check (GSource *source)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+	GHashTableIter h_iter;
+	gboolean some_ready;
+	PollData *poll_data;
+
+	nm_assert (ctx_src->context);
+
+	_CTX_LOG ("check");
+
+	_ctx_integ_source_reacquire (ctx_src);
+
+	g_hash_table_iter_init (&h_iter, ctx_src->fds);
+	while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) {
+		guint revents;
+
+		revents = g_source_query_unix_fd (&ctx_src->source, poll_data->tag);
+		if (G_UNLIKELY (poll_data->has_many_idx)) {
+			int num = poll_data->idx.many[1];
+			int *p_idx = &poll_data->idx.many[2];
+
+			for (; num > 0; num--, p_idx++)
+				ctx_src->fds_arr[*p_idx].revents = revents;
+		} else
+			ctx_src->fds_arr[poll_data->idx.one].revents = revents;
+	}
+
+	some_ready = g_main_context_check (ctx_src->context,
+	                                   ctx_src->max_priority,
+	                                   ctx_src->fds_arr,
+	                                   ctx_src->fds_len);
+
+	_CTX_LOG ("check (some-ready=%d)...", some_ready);
+
+	return some_ready;
+}
+
+static gboolean
+_ctx_integ_source_dispatch (GSource *source,
+                            GSourceFunc callback,
+                            gpointer user_data)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+
+	nm_assert (ctx_src->context);
+
+	_ctx_integ_source_reacquire (ctx_src);
+
+	_CTX_LOG ("dispatch");
+
+	g_main_context_dispatch (ctx_src->context);
+
+	return G_SOURCE_CONTINUE;
+}
+
+static void
+_ctx_integ_source_finalize (GSource *source)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+	GHashTableIter h_iter;
+	PollData *poll_data;
+
+	g_return_if_fail (ctx_src->context);
+
+	_CTX_LOG ("finalize...");
+
+	g_hash_table_iter_init (&h_iter, ctx_src->fds);
+	while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) {
+		nm_assert (poll_data->tag);
+		_CTX_LOG ("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+		g_source_remove_unix_fd (&ctx_src->source, poll_data->tag);
+		g_hash_table_iter_remove (&h_iter);
+	}
+
+	nm_clear_pointer (&ctx_src->fds, g_hash_table_unref);
+	nm_clear_g_free (&ctx_src->fds_arr);
+	ctx_src->fds_len = 0;
+
+	if (ctx_src->acquired) {
+		ctx_src->acquired = FALSE;
+		g_main_context_release (ctx_src->context);
+	}
+
+	nm_clear_pointer (&ctx_src->context, g_main_context_unref);
+}
+
+static GSourceFuncs ctx_integ_source_funcs = {
+	.prepare  = _ctx_integ_source_prepare,
+	.check    = _ctx_integ_source_check,
+	.dispatch = _ctx_integ_source_dispatch,
+	.finalize = _ctx_integ_source_finalize,
+};
+
+/**
+ * nm_utils_g_main_context_create_integrate_source:
+ * @inner_context: the inner context that will be integrated to an
+ *   outer #GMainContext.
+ *
+ * By integrating the inner context with an outer context, when iterating the outer
+ * context sources on the inner context will be dispatched. Note that while the
+ * created source exists, the @inner_context will be acquired. The user gets restricted
+ * what to do with the inner context. In particular while the inner context is integrated,
+ * the user should not acquire the inner context again or explicitly iterate it. What
+ * the user of course still can (and wants to) do is attaching new sources to the inner
+ * context.
+ *
+ * Note that GSource has a priority. While each context dispatches events based on
+ * their source's priorities, the outer context dispatches to the inner context
+ * only with one priority (the priority of the created source). That is, the sources
+ * from the two contexts are kept separate and are not sorted by their priorities.
+ *
+ * Returns: a newly created GSource that should be attached to the
+ *   outer context.
+ */
+GSource *
+nm_utils_g_main_context_create_integrate_source (GMainContext *inner_context)
+{
+	CtxIntegSource *ctx_src;
+
+	g_return_val_if_fail (inner_context, NULL);
+
+	if (!g_main_context_acquire (inner_context)) {
+		/* We require to acquire the context while it's integrated. We need to keep it acquired
+		 * for the entire duration.
+		 *
+		 * This is also necessary because g_source_attach() only wakes up the context, if
+		 * the context is currently acquired. */
+		g_return_val_if_reached (NULL);
+	}
+
+	ctx_src = (CtxIntegSource *) g_source_new (&ctx_integ_source_funcs, sizeof (CtxIntegSource));
+
+	g_source_set_name (&ctx_src->source, "ContextIntegrateSource");
+
+	ctx_src->context = g_main_context_ref (inner_context);
+	ctx_src->fds = g_hash_table_new_full (nm_pint_hash, nm_pint_equals, _poll_data_free, NULL);
+	ctx_src->fds_len = 0;
+	ctx_src->fds_arr = NULL;
+	ctx_src->acquired = TRUE;
+	ctx_src->max_priority = G_MAXINT;
+
+	_CTX_LOG ("create new integ-source for %p", inner_context);
+
+	return &ctx_src->source;
+}
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index d9c430d4..3bd1afae 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_SHARED_UTILS_H__
@@ -104,6 +89,16 @@ nm_utils_addr_family_to_size (int addr_family)
 	g_return_val_if_reached (0);
 }
 
+static inline int
+nm_utils_addr_family_from_size (gsize len)
+{
+	switch (len) {
+	case sizeof (in_addr_t):       return AF_INET;
+	case sizeof (struct in6_addr): return AF_INET6;
+	}
+	return AF_UNSPEC;
+}
+
 #define nm_assert_addr_family(addr_family) \
 	nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6))
 
@@ -537,15 +532,29 @@ nm_utils_escaped_tokens_escape_gstr (const char *str,
 guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix);
 guint32 _nm_utils_ip4_get_default_prefix (guint32 ip);
 
+gconstpointer          nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen);
+in_addr_t              nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
+const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen);
+int nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen);
+
 gboolean nm_utils_ip_is_site_local (int addr_family,
                                     const void *address);
 
 /*****************************************************************************/
 
-gboolean nm_utils_parse_inaddr_bin  (int addr_family,
-                                     const char *text,
-                                     int *out_addr_family,
-                                     gpointer out_addr);
+gboolean nm_utils_parse_inaddr_bin_full (int addr_family,
+                                         gboolean accept_legacy,
+                                         const char *text,
+                                         int *out_addr_family,
+                                         gpointer out_addr);
+static inline gboolean
+nm_utils_parse_inaddr_bin (int addr_family,
+                           const char *text,
+                           int *out_addr_family,
+                           gpointer out_addr)
+{
+	return nm_utils_parse_inaddr_bin_full (addr_family, FALSE, text, out_addr_family, out_addr);
+}
 
 gboolean nm_utils_parse_inaddr (int addr_family,
                                 const char *text,
@@ -652,8 +661,9 @@ _nm_g_slice_free_fcn_define (8)
 _nm_g_slice_free_fcn_define (10)
 _nm_g_slice_free_fcn_define (12)
 _nm_g_slice_free_fcn_define (16)
+_nm_g_slice_free_fcn_define (32)
 
-#define _nm_g_slice_free_fcn1(mem_size) \
+#define nm_g_slice_free_fcn1(mem_size) \
 	({ \
 		void (*_fcn) (gpointer); \
 		\
@@ -666,7 +676,8 @@ _nm_g_slice_free_fcn_define (16)
 		                      || ((mem_size) ==  8) \
 		                      || ((mem_size) == 10) \
 		                      || ((mem_size) == 12) \
-		                      || ((mem_size) == 16)); \
+		                      || ((mem_size) == 16) \
+		                      || ((mem_size) == 32)); \
 		switch ((mem_size)) { \
 		case  1: _fcn = _nm_g_slice_free_fcn_1;  break; \
 		case  2: _fcn = _nm_g_slice_free_fcn_2;  break; \
@@ -675,6 +686,7 @@ _nm_g_slice_free_fcn_define (16)
 		case 10: _fcn = _nm_g_slice_free_fcn_10; break; \
 		case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
 		case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
+		case 32: _fcn = _nm_g_slice_free_fcn_32; break; \
 		default: g_assert_not_reached (); _fcn = NULL; break; \
 		} \
 		_fcn; \
@@ -688,14 +700,38 @@ _nm_g_slice_free_fcn_define (16)
  * Returns: a function pointer with GDestroyNotify signature
  *   for g_slice_free(type,*).
  *
- * Only certain types are implemented. You'll get an assertion
- * using the wrong type. */
-#define nm_g_slice_free_fcn(type) (_nm_g_slice_free_fcn1 (sizeof (type)))
+ * Only certain types are implemented. You'll get a compile time
+ * error for the wrong types. */
+#define nm_g_slice_free_fcn(type) (nm_g_slice_free_fcn1 (sizeof (type)))
 
 #define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64))
 
 /*****************************************************************************/
 
+static inline void
+nm_g_set_error_take (GError **error, GError *error_take)
+{
+	if (!error_take)
+		g_return_if_reached ();
+	if (!error) {
+		g_error_free (error_take);
+		return;
+	}
+	if (*error) {
+		g_error_free (error_take);
+		g_return_if_reached ();
+	}
+	*error = error_take;
+}
+
+#define nm_g_set_error_take_lazy(error, error_take_lazy) \
+	G_STMT_START { \
+		GError **_error = (error); \
+		\
+		if (_error) \
+			nm_g_set_error_take (_error, (error_take_lazy)); \
+	} G_STMT_END
+
 /**
  * NMUtilsError:
  * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error
@@ -750,6 +786,17 @@ GQuark nm_utils_error_quark (void);
 void nm_utils_error_set_cancelled (GError **error,
                                    gboolean is_disposing,
                                    const char *instance_name);
+
+static inline GError *
+nm_utils_error_new_cancelled (gboolean is_disposing,
+                              const char *instance_name)
+{
+	GError *error = NULL;
+
+	nm_utils_error_set_cancelled (&error, is_disposing, instance_name);
+	return error;
+}
+
 gboolean nm_utils_error_is_cancelled (GError *error,
                                       gboolean consider_is_disposing);
 
@@ -792,6 +839,11 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
 		                                   sizeof (_bstrerr))); \
 	} G_STMT_END
 
+#define nm_utils_error_new(error_code, ...) \
+	(   (NM_NARG (__VA_ARGS__) == 1) \
+	 ? g_error_new_literal (NM_UTILS_ERROR, (error_code), _NM_UTILS_MACRO_FIRST (__VA_ARGS__)) \
+	 : g_error_new         (NM_UTILS_ERROR, (error_code), __VA_ARGS__))
+
 /*****************************************************************************/
 
 gboolean nm_g_object_set_property (GObject *object,
@@ -906,6 +958,82 @@ nm_g_variant_unref_floating (GVariant *var)
 		g_variant_unref (var);
 }
 
+static inline void
+nm_g_source_destroy_and_unref (GSource *source)
+{
+	g_source_destroy (source);
+	g_source_unref (source);
+}
+
+#define nm_clear_g_source_inst(ptr) (nm_clear_pointer ((ptr), nm_g_source_destroy_and_unref))
+
+NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_destroy_and_unref_gsource, nm_g_source_destroy_and_unref);
+#define nm_auto_destroy_and_unref_gsource nm_auto(_nm_auto_destroy_and_unref_gsource)
+
+NM_AUTO_DEFINE_FCN0 (GMainContext *, _nm_auto_pop_gmaincontext, g_main_context_pop_thread_default)
+#define nm_auto_pop_gmaincontext nm_auto (_nm_auto_pop_gmaincontext)
+
+GSource *nm_g_idle_source_new (int priority,
+                               GSourceFunc func,
+                               gpointer user_data,
+                               GDestroyNotify destroy_notify);
+
+GSource *nm_g_timeout_source_new (guint timeout_ms,
+                                  int priority,
+                                  GSourceFunc func,
+                                  gpointer user_data,
+                                  GDestroyNotify destroy_notify);
+
+GSource *nm_g_unix_signal_source_new (int signum,
+                                      int priority,
+                                      GSourceFunc handler,
+                                      gpointer user_data,
+                                      GDestroyNotify notify);
+
+static inline GSource *
+nm_g_source_attach (GSource *source,
+                    GMainContext *context)
+{
+	g_source_attach (source, context);
+	return source;
+}
+
+NM_AUTO_DEFINE_FCN0 (GMainContext *, _nm_auto_unref_gmaincontext, g_main_context_unref)
+#define nm_auto_unref_gmaincontext nm_auto (_nm_auto_unref_gmaincontext)
+
+static inline GMainContext *
+nm_g_main_context_push_thread_default (GMainContext *context)
+{
+	/* This function is to work together with nm_auto_pop_gmaincontext. */
+	if (G_UNLIKELY (!context))
+		context = g_main_context_default ();
+	g_main_context_push_thread_default (context);
+	return context;
+}
+
+static inline GMainContext *
+nm_g_main_context_push_thread_default_if_necessary (GMainContext *context)
+{
+	GMainContext *cur_context;
+
+	cur_context = g_main_context_get_thread_default ();
+	if (cur_context == context)
+		return NULL;
+
+	if (G_UNLIKELY (!cur_context)) {
+		cur_context = g_main_context_default ();
+		if (cur_context == context)
+			return NULL;
+	} else if (G_UNLIKELY (!context)) {
+		context = g_main_context_default ();
+		if (cur_context == context)
+			return NULL;
+	}
+
+	g_main_context_push_thread_default (context);
+	return context;
+}
+
 /*****************************************************************************/
 
 static inline int
@@ -966,6 +1094,11 @@ gpointer *nm_utils_hash_keys_to_array (GHashTable *hash,
                                        gpointer user_data,
                                        guint *out_len);
 
+gpointer *nm_utils_hash_values_to_array (GHashTable *hash,
+                                         GCompareDataFunc compare_func,
+                                         gpointer user_data,
+                                         guint *out_len);
+
 static inline const char **
 nm_utils_strdict_get_keys (const GHashTable *hash,
                            gboolean sorted,
@@ -990,7 +1123,9 @@ nm_utils_strv_make_deep_copied_nonnull (const char **strv)
 	return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1);
 }
 
-char **nm_utils_strv_dup (gpointer strv, gssize len);
+char **nm_utils_strv_dup (gpointer strv,
+                          gssize len,
+                          gboolean deep_copied);
 
 /*****************************************************************************/
 
@@ -999,6 +1134,8 @@ GSList *nm_utils_g_slist_find_str (const GSList *list,
 
 int nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b);
 
+char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator);
+
 /*****************************************************************************/
 
 gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list,
@@ -1160,6 +1297,15 @@ nm_utils_dbus_normalize_object_path (const char *path)
 
 guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
 
+static inline gboolean
+nm_utils_process_state_is_dead (char pstate)
+{
+	/* "/proc/[pid]/stat" returns a state as the 3rd fields (see `man 5 proc`).
+	 * Some of these states indicate the the process is effectively dead (or a zombie).
+	 */
+	return NM_IN_SET (pstate, 'Z', 'x', 'X');
+}
+
 /*****************************************************************************/
 
 gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args);
@@ -1183,6 +1329,10 @@ void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback,
 
 /*****************************************************************************/
 
+GSource *nm_utils_g_main_context_create_integrate_source (GMainContext *internal);
+
+/*****************************************************************************/
+
 static inline void
 nm_strv_ptrarray_add_string_take (GPtrArray *cmd,
                                   char *str)
@@ -1252,4 +1402,34 @@ guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr,
                                    gsize required_len,
                                    gsize *out_len);
 
+/*****************************************************************************/
+
+static inline GTask *
+nm_g_task_new (gpointer source_object,
+               GCancellable *cancellable,
+               gpointer source_tag,
+               GAsyncReadyCallback callback,
+               gpointer callback_data)
+{
+	GTask *task;
+
+	task = g_task_new (source_object, cancellable, callback, callback_data);
+	if (source_tag)
+		g_task_set_source_tag (task, source_tag);
+	return task;
+}
+
+static inline gboolean
+nm_g_task_is_valid (gpointer task,
+                    gpointer source_object,
+                    gpointer source_tag)
+{
+	return    g_task_is_valid (task, source_object)
+	       && g_task_get_source_tag (task) == source_tag;
+}
+
+guint nm_utils_parse_debug_string (const char *string,
+                                   const GDebugKey *keys,
+                                   guint nkeys);
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c
index 7735f29d..20d663bc 100644
--- a/shared/nm-glib-aux/nm-time-utils.c
+++ b/shared/nm-glib-aux/nm-time-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -234,12 +219,17 @@ nm_utils_get_monotonic_timestamp_s (void)
  * @timestamp_ns_per_tick: How many nanoseconds make one unit of @timestamp? E.g. if
  *   @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; if @timestamp is
  *   in nanoseconds, pass 1; if @timestamp is in milliseconds, pass %NM_UTILS_NS_PER_SECOND/1000.
+ *   This must be a multiple of 10, and between 1 and %NM_UTILS_NS_PER_SECOND.
  *
  * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime().
  *   The unit is the same as the passed in @timestamp based on @timestamp_ns_per_tick.
  *   E.g. if you passed @timestamp in as seconds, it will return boottime in seconds.
- *   If @timestamp is non-positive, it returns -1. Note that a (valid) monotonic-timestamp
- *   is always positive.
+ *
+ *   Note that valid monotonic-timestamps are always positive numbers (counting roughly since
+ *   the application is running). However, it might make sense to calculate a timestamp from
+ *   before the application was running, hence negative @timestamp is allowed. The result
+ *   in that case might also be a negative timestamp (in CLOCK_BOOTTIME), which would indicate
+ *   that the timestamp lies in the past before the machine was booted.
  *
  * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC.
  **/
@@ -256,9 +246,6 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_
 	                          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. */
 	nm_assert (g_atomic_pointer_get (&p_global_state));
@@ -270,12 +257,60 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_
 	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
 	offset = p->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);
+	nm_assert (offset <= 0 && offset > G_MININT64);
+
+	/* check for overflow (note that offset is non-positive). */
+	g_return_val_if_fail (timestamp < G_MAXINT64 + offset, G_MAXINT64);
 
 	return timestamp - offset;
 }
 
+/**
+ * nm_utils_monotonic_timestamp_from_boottime:
+ * @boottime: the timestamp from CLOCK_BOOTTIME (or CLOCK_MONOTONIC, if
+ *   kernel does not support CLOCK_BOOTTIME and monotonic timestamps are based
+ *   on CLOCK_MONOTONIC).
+ * @timestamp_ns_per_tick: the scale in which @boottime is. If @boottime is in
+ *   nano seconds, this should be 1. If it is in milli seconds, this should be
+ *   %NM_UTILS_NS_PER_SECOND/1000, etc.
+ *
+ * Returns: the same timestamp in monotonic timestamp scale.
+ *
+ * Note that commonly monotonic timestamps are positive. But they may not
+ * be positive in this case. That's when boottime is taken from a time before
+ * the monotonic timestamps started counting. So, that means a zero or negative
+ * value is still a valid timestamp.
+ *
+ * This is the inverse of nm_utils_monotonic_timestamp_as_boottime().
+ */
+gint64
+nm_utils_monotonic_timestamp_from_boottime (guint64 boottime, gint64 timestamp_ns_per_tick)
+{
+	const GlobalState *p;
+	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);
+
+	p = _t_get_global_state ();
+
+	nm_assert (p->offset_sec <= 0);
+
+	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
+	offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick);
+
+	nm_assert (offset <= 0 && offset > G_MININT64);
+
+	/* check for overflow (note that offset is non-positive). */
+	g_return_val_if_fail (boottime < G_MAXINT64, G_MAXINT64);
+
+	return (gint64) boottime + offset;
+}
+
 gint64
 nm_utils_clock_gettime_ns (clockid_t clockid)
 {
diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h
index 52d6637d..8bf41b96 100644
--- a/shared/nm-glib-aux/nm-time-utils.h
+++ b/shared/nm-glib-aux/nm-time-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_TIME_UTILS_H__
@@ -42,6 +27,7 @@ gint64 nm_utils_get_monotonic_timestamp_us (void);
 gint64 nm_utils_get_monotonic_timestamp_ms (void);
 gint32 nm_utils_get_monotonic_timestamp_s (void);
 gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns);
+gint64 nm_utils_monotonic_timestamp_from_boottime (guint64 boottime, gint64 timestamp_ns_per_tick);
 
 static inline gint64
 nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now)
diff --git a/shared/nm-glib-aux/nm-value-type.h b/shared/nm-glib-aux/nm-value-type.h
index b4d6898f..6fe6f072 100644
--- a/shared/nm-glib-aux/nm-value-type.h
+++ b/shared/nm-glib-aux/nm-value-type.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_VALUE_TYPE_H__