summary refs log tree commit diff
path: root/shared/nm-glib-aux
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-02-11 18:11:46 +0100
committerMichael Biebl <biebl@debian.org>2021-02-11 18:11:46 +0100
commit80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch)
treee3b229aa94e8dcf0590f2317664176e7b8f7607b /shared/nm-glib-aux
parent65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff)
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'shared/nm-glib-aux')
-rw-r--r--shared/nm-glib-aux/nm-c-list.h7
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.c4
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.h2
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.c4
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.h2
-rw-r--r--shared/nm-glib-aux/nm-default-glib-i18n-lib.h21
-rw-r--r--shared/nm-glib-aux/nm-default-glib-i18n-prog.h21
-rw-r--r--shared/nm-glib-aux/nm-default-glib.h75
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.c58
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.h2
-rw-r--r--shared/nm-glib-aux/nm-errno.c6
-rw-r--r--shared/nm-glib-aux/nm-errno.h2
-rw-r--r--shared/nm-glib-aux/nm-gassert-patch.h76
-rw-r--r--shared/nm-glib-aux/nm-glib.h4
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.c4
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.h10
-rw-r--r--shared/nm-glib-aux/nm-io-utils.c4
-rw-r--r--shared/nm-glib-aux/nm-io-utils.h2
-rw-r--r--shared/nm-glib-aux/nm-jansson.h2
-rw-r--r--shared/nm-glib-aux/nm-json-aux.c14
-rw-r--r--shared/nm-glib-aux/nm-json-aux.h2
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.c4
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.h2
-rw-r--r--shared/nm-glib-aux/nm-logging-base.c4
-rw-r--r--shared/nm-glib-aux/nm-logging-base.h2
-rw-r--r--shared/nm-glib-aux/nm-logging-fwd.h12
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h32
-rw-r--r--shared/nm-glib-aux/nm-obj.h2
-rw-r--r--shared/nm-glib-aux/nm-random-utils.c4
-rw-r--r--shared/nm-glib-aux/nm-random-utils.h2
-rw-r--r--shared/nm-glib-aux/nm-ref-string.c21
-rw-r--r--shared/nm-glib-aux/nm-ref-string.h2
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.c4
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.h2
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c316
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h287
-rw-r--r--shared/nm-glib-aux/nm-str-buf.h61
-rw-r--r--shared/nm-glib-aux/nm-time-utils.c4
-rw-r--r--shared/nm-glib-aux/nm-time-utils.h2
-rw-r--r--shared/nm-glib-aux/nm-value-type.h2
-rw-r--r--shared/nm-glib-aux/tests/meson.build12
-rw-r--r--shared/nm-glib-aux/tests/test-json-aux.c6
-rw-r--r--shared/nm-glib-aux/tests/test-shared-general.c78
43 files changed, 903 insertions, 280 deletions
diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h
index e19774dd..6dd3ac72 100644
--- a/shared/nm-glib-aux/nm-c-list.h
+++ b/shared/nm-glib-aux/nm-c-list.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2014 Red Hat, Inc.
  */
@@ -17,11 +17,6 @@
         _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 {
diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c
index e47797a8..ec409ff1 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.c
+++ b/shared/nm-glib-aux/nm-dbus-aux.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2019 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-dbus-aux.h"
 
diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h
index 7b0b008d..4e3ae22d 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.h
+++ b/shared/nm-glib-aux/nm-dbus-aux.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2019 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c
index 4a16f850..99da3b35 100644
--- a/shared/nm-glib-aux/nm-dedup-multi.c
+++ b/shared/nm-glib-aux/nm-dedup-multi.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-dedup-multi.h"
 
diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h
index 6941dc63..1c0761bf 100644
--- a/shared/nm-glib-aux/nm-dedup-multi.h
+++ b/shared/nm-glib-aux/nm-dedup-multi.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-default-glib-i18n-lib.h b/shared/nm-glib-aux/nm-default-glib-i18n-lib.h
new file mode 100644
index 00000000..9393d192
--- /dev/null
+++ b/shared/nm-glib-aux/nm-default-glib-i18n-lib.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_DEFAULT_GLIB_I18N_LIB_H__
+#define __NM_DEFAULT_GLIB_I18N_LIB_H__
+
+/*****************************************************************************/
+
+#define _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB
+
+#include "nm-glib-aux/nm-default-glib.h"
+
+#undef NETWORKMANAGER_COMPILATION
+#define NETWORKMANAGER_COMPILATION \
+    (NM_NETWORKMANAGER_COMPILATION_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB)
+
+/*****************************************************************************/
+
+#endif /* __NM_DEFAULT_GLIB_I18N_LIB_H__ */
diff --git a/shared/nm-glib-aux/nm-default-glib-i18n-prog.h b/shared/nm-glib-aux/nm-default-glib-i18n-prog.h
new file mode 100644
index 00000000..0abe807b
--- /dev/null
+++ b/shared/nm-glib-aux/nm-default-glib-i18n-prog.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_DEFAULT_GLIB_I18N_PROG_H__
+#define __NM_DEFAULT_GLIB_I18N_PROG_H__
+
+/*****************************************************************************/
+
+#define _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG
+
+#include "nm-glib-aux/nm-default-glib.h"
+
+#undef NETWORKMANAGER_COMPILATION
+#define NETWORKMANAGER_COMPILATION \
+    (NM_NETWORKMANAGER_COMPILATION_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)
+
+/*****************************************************************************/
+
+#endif /* __NM_DEFAULT_GLIB_I18N_PROG_H__ */
diff --git a/shared/nm-glib-aux/nm-default-glib.h b/shared/nm-glib-aux/nm-default-glib.h
new file mode 100644
index 00000000..34b23f77
--- /dev/null
+++ b/shared/nm-glib-aux/nm-default-glib.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_DEFAULT_GLIB_H__
+#define __NM_DEFAULT_GLIB_H__
+
+/*****************************************************************************/
+
+#include "nm-std-aux/nm-default-std.h"
+
+#undef NETWORKMANAGER_COMPILATION
+#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_WITH_GLIB
+
+/*****************************************************************************/
+
+#include <glib.h>
+
+#if defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG)
+    #if defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB)
+        #error Cannot define _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB and _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG together
+    #endif
+    #undef _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG
+    #include <glib/gi18n.h>
+#elif defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB)
+    #undef _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB
+    #include <glib/gi18n-lib.h>
+#endif
+
+/*****************************************************************************/
+
+#if NM_MORE_ASSERTS == 0
+    #ifndef G_DISABLE_CAST_CHECKS
+        /* Unless compiling with G_DISABLE_CAST_CHECKS, glib performs type checking
+         * during G_VARIANT_TYPE() via g_variant_type_checked_(). This is not necessary
+         * because commonly this cast is needed during something like
+         *
+         *   g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}"));
+         *
+         * Note that in if the variant type would be invalid, the check still
+         * wouldn't make the buggy code magically work. Instead of passing a
+         * bogus type string (bad), it would pass %NULL to g_variant_builder_init()
+         * (also bad).
+         *
+         * Also, a function like g_variant_builder_init() already validates
+         * the input type via something like
+         *
+         *   g_return_if_fail (g_variant_type_is_container (type));
+         *
+         * So, by having G_VARIANT_TYPE() also validate the type, we validate
+         * twice, whereas the first validation is rather pointless because it
+         * doesn't prevent the function to be called with invalid arguments.
+         *
+         * Just patch G_VARIANT_TYPE() to perform no check.
+         */
+        #undef G_VARIANT_TYPE
+        #define G_VARIANT_TYPE(type_string) ((const GVariantType *) (type_string))
+    #endif
+#endif
+
+/*****************************************************************************/
+
+#include "nm-gassert-patch.h"
+
+#include "nm-std-aux/nm-std-aux.h"
+#include "nm-std-aux/nm-std-utils.h"
+#include "nm-glib-aux/nm-macros-internal.h"
+#include "nm-glib-aux/nm-shared-utils.h"
+#include "nm-glib-aux/nm-errno.h"
+#include "nm-glib-aux/nm-hash-utils.h"
+
+/*****************************************************************************/
+
+#endif /* __NM_DEFAULT_GLIB_H__ */
diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c
index 5292755d..b06f2bb2 100644
--- a/shared/nm-glib-aux/nm-enum-utils.c
+++ b/shared/nm-glib-aux/nm-enum-utils.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-enum-utils.h"
 #include "nm-str-buf.h"
@@ -76,30 +76,34 @@ _ASSERT_enum_values_info(GType type, const NMUtilsEnumValueInfo *value_infos)
 }
 
 static gboolean
-_is_hex_string(const char *str)
+_is_hex_string(const char *str, gboolean allow_sign)
 {
+    if (allow_sign && str[0] == '-')
+        str++;
     return str[0] == '0' && str[1] == 'x' && str[2]
            && NM_STRCHAR_ALL(&str[2], ch, g_ascii_isxdigit(ch));
 }
 
 static gboolean
-_is_dec_string(const char *str)
+_is_dec_string(const char *str, gboolean allow_sign)
 {
+    if (allow_sign && str[0] == '-')
+        str++;
     return str[0] && NM_STRCHAR_ALL(&str[0], ch, g_ascii_isdigit(ch));
 }
 
 static gboolean
 _enum_is_valid_enum_nick(const char *str)
 {
-    return str[0] && !NM_STRCHAR_ANY(str, ch, g_ascii_isspace(ch)) && !_is_dec_string(str)
-           && !_is_hex_string(str);
+    return str[0] && !NM_STRCHAR_ANY(str, ch, g_ascii_isspace(ch)) && !_is_dec_string(str, TRUE)
+           && !_is_hex_string(str, TRUE);
 }
 
 static gboolean
 _enum_is_valid_flags_nick(const char *str)
 {
-    return str[0] && !NM_STRCHAR_ANY(str, ch, IS_FLAGS_SEPARATOR(ch)) && !_is_dec_string(str)
-           && !_is_hex_string(str);
+    return str[0] && !NM_STRCHAR_ANY(str, ch, IS_FLAGS_SEPARATOR(ch)) && !_is_dec_string(str, FALSE)
+           && !_is_hex_string(str, FALSE);
 }
 
 char *
@@ -212,8 +216,7 @@ _nm_utils_enum_from_str_full(GType                       type,
 
     _ASSERT_enum_values_info(type, value_infos);
 
-    str_clone = strdup(str);
-    s         = nm_str_skip_leading_spaces(str_clone);
+    s = nm_strdup_maybe_a(300, nm_str_skip_leading_spaces(str), &str_clone);
     g_strchomp(s);
 
     klass = g_type_class_ref(type);
@@ -221,16 +224,31 @@ _nm_utils_enum_from_str_full(GType                       type,
     if (G_IS_ENUM_CLASS(klass)) {
         GEnumValue *enum_value;
 
+        G_STATIC_ASSERT(G_MAXINT < G_MAXINT64);
+        G_STATIC_ASSERT(G_MININT > G_MININT64);
+
         if (s[0]) {
-            if (_is_hex_string(s)) {
-                v64 = _nm_utils_ascii_str_to_int64(s, 16, 0, G_MAXUINT, -1);
-                if (v64 != -1) {
-                    value = (int) v64;
-                    ret   = TRUE;
+            if (_is_hex_string(s, TRUE)) {
+                if (s[0] == '-') {
+                    v64 = _nm_utils_ascii_str_to_int64(&s[3],
+                                                       16,
+                                                       -((gint64) G_MAXINT),
+                                                       -((gint64) G_MININT),
+                                                       G_MAXINT64);
+                    if (v64 != G_MAXINT64) {
+                        value = (int) (-v64);
+                        ret   = TRUE;
+                    }
+                } else {
+                    v64 = _nm_utils_ascii_str_to_int64(&s[2], 16, G_MININT, G_MAXINT, G_MAXINT64);
+                    if (v64 != G_MAXINT64) {
+                        value = (int) v64;
+                        ret   = TRUE;
+                    }
                 }
-            } else if (_is_dec_string(s)) {
-                v64 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT, -1);
-                if (v64 != -1) {
+            } else if (_is_dec_string(s, TRUE)) {
+                v64 = _nm_utils_ascii_str_to_int64(s, 10, G_MININT, G_MAXINT, G_MAXINT64);
+                if (v64 != G_MAXINT64) {
                     value = (int) v64;
                     ret   = TRUE;
                 }
@@ -259,14 +277,14 @@ _nm_utils_enum_from_str_full(GType                       type,
             }
 
             if (s[0]) {
-                if (_is_hex_string(s)) {
+                if (_is_hex_string(s, FALSE)) {
                     v64 = _nm_utils_ascii_str_to_int64(&s[2], 16, 0, G_MAXUINT, -1);
                     if (v64 == -1) {
                         ret = FALSE;
                         break;
                     }
                     uvalue |= (unsigned) v64;
-                } else if (_is_dec_string(s)) {
+                } else if (_is_dec_string(s, FALSE)) {
                     v64 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT, -1);
                     if (v64 == -1) {
                         ret = FALSE;
diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h
index 38acf8fd..89be54e7 100644
--- a/shared/nm-glib-aux/nm-enum-utils.h
+++ b/shared/nm-glib-aux/nm-enum-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c
index f7a7685d..668606ca 100644
--- a/shared/nm-glib-aux/nm-errno.c
+++ b/shared/nm-glib-aux/nm-errno.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-errno.h"
 
@@ -106,7 +106,7 @@ nm_strerror_native_r(int errsv, char *buf, gsize buf_size)
     nm_assert(buf);
     nm_assert(buf_size > 0);
 
-#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE
+#if (!defined(__GLIBC__) && !defined(__UCLIBC__)) || ((_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE)
     /* XSI-compliant */
     {
         int errno_saved = errno;
diff --git a/shared/nm-glib-aux/nm-errno.h b/shared/nm-glib-aux/nm-errno.h
index bf7189c0..62c8379f 100644
--- a/shared/nm-glib-aux/nm-errno.h
+++ b/shared/nm-glib-aux/nm-errno.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-gassert-patch.h b/shared/nm-glib-aux/nm-gassert-patch.h
new file mode 100644
index 00000000..bac8697c
--- /dev/null
+++ b/shared/nm-glib-aux/nm-gassert-patch.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_GASSERT_PATCH_H__
+#define __NM_GASSERT_PATCH_H__
+
+/*****************************************************************************/
+
+#if NM_MORE_ASSERTS == 0
+
+/* glib assertions (g_return_*(), g_assert*()) contain a textual representation
+ * of the checked statement. This part of the assertion blows up the size of the
+ * binary. Unless we compile a debug-build with NM_MORE_ASSERTS, drop these
+ * parts. Note that the failed assertion still prints the file and line where the
+ * assertion fails. That shall suffice. */
+
+static inline void
+_nm_g_return_if_fail_warning(const char *log_domain, const char *file, int line)
+{
+    char file_buf[256 + 15];
+
+    g_snprintf(file_buf, sizeof(file_buf), "((%s:%d))", file, line);
+    g_return_if_fail_warning(log_domain, file_buf, "<dropped>");
+}
+
+    #define g_return_if_fail_warning(log_domain, pretty_function, expression) \
+        _nm_g_return_if_fail_warning(log_domain, __FILE__, __LINE__)
+
+    #define g_assertion_message_expr(domain, file, line, func, expr) \
+        g_assertion_message_expr(domain, file, line, "<unknown-fcn>", (expr) ? "<dropped>" : NULL)
+
+    #undef g_return_val_if_reached
+    #define g_return_val_if_reached(val)                          \
+        G_STMT_START                                              \
+        {                                                         \
+            g_log(G_LOG_DOMAIN,                                   \
+                  G_LOG_LEVEL_CRITICAL,                           \
+                  "file %s: line %d (%s): should not be reached", \
+                  __FILE__,                                       \
+                  __LINE__,                                       \
+                  "<dropped>");                                   \
+            return (val);                                         \
+        }                                                         \
+        G_STMT_END
+
+    #undef g_return_if_reached
+    #define g_return_if_reached()                                 \
+        G_STMT_START                                              \
+        {                                                         \
+            g_log(G_LOG_DOMAIN,                                   \
+                  G_LOG_LEVEL_CRITICAL,                           \
+                  "file %s: line %d (%s): should not be reached", \
+                  __FILE__,                                       \
+                  __LINE__,                                       \
+                  "<dropped>");                                   \
+            return;                                               \
+        }                                                         \
+        G_STMT_END
+#endif
+
+/*****************************************************************************/
+
+#if NM_MORE_ASSERTS == 0
+    #define NM_ASSERT_G_RETURN_EXPR(expr) "<dropped>"
+    #define NM_ASSERT_NO_MSG              1
+
+#else
+    #define NM_ASSERT_G_RETURN_EXPR(expr) "" expr ""
+    #define NM_ASSERT_NO_MSG              0
+#endif
+
+/*****************************************************************************/
+
+#endif /* __NM_GASSERT_PATCH_H__ */
diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h
index 9794e1fb..befb8d90 100644
--- a/shared/nm-glib-aux/nm-glib.h
+++ b/shared/nm-glib-aux/nm-glib.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2008 - 2018 Red Hat, Inc.
  */
@@ -683,7 +683,7 @@ g_hash_table_steal_extended(GHashTable *  hash_table,
             gpointer *_stolen_value = (stolen_value);                                        \
                                                                                              \
             /* we cannot allow NULL arguments, because then we would leak the values in
-         * the compat implementation. */      \
+             * the compat implementation. */      \
             g_assert(_stolen_key);                                                           \
             g_assert(_stolen_value);                                                         \
                                                                                              \
diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c
index 8a1c87fd..29349b1d 100644
--- a/shared/nm-glib-aux/nm-hash-utils.c
+++ b/shared/nm-glib-aux/nm-hash-utils.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-hash-utils.h"
 
diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h
index 1cfdcaf6..a7b8677b 100644
--- a/shared/nm-glib-aux/nm-hash-utils.h
+++ b/shared/nm-glib-aux/nm-hash-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
@@ -416,6 +416,14 @@ nm_hash_obfuscate_ptr(guint static_seed, gconstpointer val)
  * values in a global context. */
 #define NM_HASH_OBFUSCATE_PTR(ptr) (nm_hash_obfuscate_ptr(1678382159u, ptr))
 
+#define NM_HASH_OBFUSCATE_PTR_STR(ptr, buf)                                                        \
+    ({                                                                                             \
+        gconstpointer _ptr = (ptr);                                                                \
+                                                                                                   \
+        _ptr ? nm_sprintf_buf(buf, "[" NM_HASH_OBFUSCATE_PTR_FMT "]", NM_HASH_OBFUSCATE_PTR(_ptr)) \
+             : "(null)";                                                                           \
+    })
+
 static inline const char *
 nm_hash_obfuscated_ptr_str(gconstpointer ptr, char buf[static 17])
 {
diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c
index 32f92156..429591ad 100644
--- a/shared/nm-glib-aux/nm-io-utils.c
+++ b/shared/nm-glib-aux/nm-io-utils.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-io-utils.h"
 
diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h
index 81be7b60..8182f5cf 100644
--- a/shared/nm-glib-aux/nm-io-utils.h
+++ b/shared/nm-glib-aux/nm-io-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h
index 9b7731a4..6173a7ac 100644
--- a/shared/nm-glib-aux/nm-jansson.h
+++ b/shared/nm-glib-aux/nm-jansson.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c
index 4212e628..97ee606f 100644
--- a/shared/nm-glib-aux/nm-json-aux.c
+++ b/shared/nm-glib-aux/nm-json-aux.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 - 2019 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-json-aux.h"
 
@@ -11,6 +11,16 @@
 
 /*****************************************************************************/
 
+/* If RTLD_DEEPBIND isn't available just ignore it. This can cause problems
+ * with jansson, json-glib, and cjson symbols clashing (and as such crashing the
+ * program). But that needs to be fixed by the json libraries, and it is by adding
+ * symbol versioning in recent versions. */
+#ifndef RTLD_DEEPBIND
+    #define RTLD_DEEPBIND 0
+#endif
+
+/*****************************************************************************/
+
 static void
 _gstr_append_string_len(GString *gstr, const char *str, gsize len)
 {
diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h
index 348f0f58..99759a8d 100644
--- a/shared/nm-glib-aux/nm-json-aux.h
+++ b/shared/nm-glib-aux/nm-json-aux.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 - 2019 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c
index ae90fb0d..b5962712 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.c
+++ b/shared/nm-glib-aux/nm-keyfile-aux.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2019 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-keyfile-aux.h"
 
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h
index 198a2c53..72d2f418 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.h
+++ b/shared/nm-glib-aux/nm-keyfile-aux.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2019 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-logging-base.c b/shared/nm-glib-aux/nm-logging-base.c
index 229470f6..66b591b2 100644
--- a/shared/nm-glib-aux/nm-logging-base.c
+++ b/shared/nm-glib-aux/nm-logging-base.c
@@ -1,6 +1,6 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-logging-base.h"
 
diff --git a/shared/nm-glib-aux/nm-logging-base.h b/shared/nm-glib-aux/nm-logging-base.h
index eb71c2a4..d9ac03c7 100644
--- a/shared/nm-glib-aux/nm-logging-base.h
+++ b/shared/nm-glib-aux/nm-logging-base.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #ifndef __NM_LOGGING_BASE_H__
 #define __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 ee75b713..df0bb161 100644
--- a/shared/nm-glib-aux/nm-logging-fwd.h
+++ b/shared/nm-glib-aux/nm-logging-fwd.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2006 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
@@ -57,15 +57,11 @@ typedef enum { /*< skip >*/
                /* aliases: */
                LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6,
                LOGD_IP   = LOGD_IP4 | LOGD_IP6,
-} NMLogDomain;
 
-static inline NMLogDomain
-LOGD_DHCP_from_addr_family(int addr_family)
-{
-    nm_assert_addr_family(addr_family);
+#define LOGD_DHCPX(is_ipv4) ((is_ipv4) ? LOGD_DHCP4 : LOGD_DHCP6)
+#define LOGD_IPX(is_ipv4)   ((is_ipv4) ? LOGD_IP4 : LOGD_IP6)
 
-    return addr_family == AF_INET6 ? LOGD_DHCP6 : LOGD_DHCP4;
-}
+} NMLogDomain;
 
 /* Log levels */
 typedef enum { /*< skip >*/
diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h
index d495dd8e..113a67a0 100644
--- a/shared/nm-glib-aux/nm-macros-internal.h
+++ b/shared/nm-glib-aux/nm-macros-internal.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2012 Colin Walters <walters@verbum.org>.
  * Copyright (C) 2014 Red Hat, Inc.
@@ -656,11 +656,11 @@ NM_G_ERROR_MSG(GError *error)
 
 #if _NM_CC_SUPPORT_GENERIC
     /* returns @value, if the type of @value matches @type.
- * This requires support for C11 _Generic(). If no support is
- * present, this returns @value directly.
- *
- * It's useful to check the let the compiler ensure that @value is
- * of a certain type. */
+     * This requires support for C11 _Generic(). If no support is
+     * present, this returns @value directly.
+     *
+     * It's useful to check the let the compiler ensure that @value is
+     * of a certain type. */
     #define _NM_ENSURE_TYPE(type, value) (_Generic((value), type : (value)))
     #define _NM_ENSURE_TYPE_CONST(type, value)              \
         (_Generic((value), const type                       \
@@ -770,7 +770,7 @@ NM_G_ERROR_MSG(GError *error)
     {                                    \
         return NM_CACHED_QUARK(string);  \
     }                                    \
-    struct _dummy_struct_for_trailing_semicolon
+    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
 
 /*****************************************************************************/
 
@@ -1038,6 +1038,14 @@ nm_g_object_unref(gpointer obj)
  */
 #define nm_clear_g_free(pp) nm_clear_pointer(pp, g_free)
 
+/* Our nm_clear_pointer() is more typesafe than g_clear_pointer() and
+ * should be preferred.
+ *
+ * For g_clear_object() that is not the case (because g_object_unref()
+ * anyway takes a void pointer). So using g_clear_object() is fine.
+ *
+ * Still have a nm_clear_g_object() because that returns a boolean
+ * indication whether anything was cleared. */
 #define nm_clear_g_object(pp) nm_clear_pointer(pp, g_object_unref)
 
 /**
@@ -1604,6 +1612,16 @@ _nm_strndup_a_step(char *s, const char *str, gsize len)
         _nm_strndup_a_step(_s_snd, _str_snd, _len_snd);            \
     })
 
+#define nm_strdup_maybe_a(alloca_maxlen, str, out_str_free)               \
+    ({                                                                    \
+        const char *const _str_snd = (str);                               \
+                                                                          \
+        (char *) nm_memdup_maybe_a(alloca_maxlen,                         \
+                                   _str_snd,                              \
+                                   _str_snd ? strlen(_str_snd) + 1u : 0u, \
+                                   out_str_free);                         \
+    })
+
 /*****************************************************************************/
 
 /* generic macro to convert an int to a (heap allocated) string.
diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h
index 9bbe05a4..2062f661 100644
--- a/shared/nm-glib-aux/nm-obj.h
+++ b/shared/nm-glib-aux/nm-obj.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c
index 0c8b2758..c95d368d 100644
--- a/shared/nm-glib-aux/nm-random-utils.c
+++ b/shared/nm-glib-aux/nm-random-utils.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-random-utils.h"
 
diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h
index 5c34d8ab..d0eae103 100644
--- a/shared/nm-glib-aux/nm-random-utils.h
+++ b/shared/nm-glib-aux/nm-random-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2017 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-ref-string.c b/shared/nm-glib-aux/nm-ref-string.c
index 526185f3..902f1c80 100644
--- a/shared/nm-glib-aux/nm-ref-string.c
+++ b/shared/nm-glib-aux/nm-ref-string.c
@@ -1,6 +1,6 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-ref-string.h"
 
@@ -168,24 +168,25 @@ void
 _nm_ref_string_unref_non_null(NMRefString *rstr)
 {
     RefString *const rstr0 = (RefString *) rstr;
+    int              r;
 
     _ASSERT(rstr0);
 
-    if (G_LIKELY(!g_atomic_int_dec_and_test(&rstr0->ref_count)))
+    /* fast-path: first try to decrement the ref-count without bringing it
+     * to zero. */
+    r = rstr0->ref_count;
+    if (G_LIKELY(r > 1 && g_atomic_int_compare_and_exchange(&rstr0->ref_count, r, r - 1)))
         return;
 
+    /* We apparently are about to return the last reference. Take a lock. */
+
     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. */
+    nm_assert(g_hash_table_lookup(gl_hash, rstr0) == rstr0);
 
-    if (g_atomic_int_get(&rstr0->ref_count) == 0) {
+    if (G_LIKELY(g_atomic_int_dec_and_test(&rstr0->ref_count))) {
         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
index fe65d6a3..97c263b0 100644
--- a/shared/nm-glib-aux/nm-ref-string.h
+++ b/shared/nm-glib-aux/nm-ref-string.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #ifndef __NM_REF_STRING_H__
 #define __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 3a488673..8188b503 100644
--- a/shared/nm-glib-aux/nm-secret-utils.c
+++ b/shared/nm-glib-aux/nm-secret-utils.c
@@ -1,10 +1,10 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  * Copyright (C) 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-secret-utils.h"
 
diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h
index 2d342e59..ac279635 100644
--- a/shared/nm-glib-aux/nm-secret-utils.h
+++ b/shared/nm-glib-aux/nm-secret-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index 7f766871..3215a33b 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-shared-utils.h"
 
@@ -19,6 +19,9 @@
 #include "nm-errno.h"
 #include "nm-str-buf.h"
 
+G_STATIC_ASSERT(sizeof(NMEtherAddr) == 6);
+G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) == 1);
+
 G_STATIC_ASSERT(sizeof(NMUtilsNamedEntry) == sizeof(const char *));
 G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, value_ptr) == sizeof(const char *));
 
@@ -361,6 +364,21 @@ again:
     return b;
 }
 
+GBytes *
+nm_g_bytes_new_from_str(const char *str)
+{
+    gsize l;
+
+    if (!str)
+        return NULL;
+
+    /* the returned array is guaranteed to have a trailing '\0'
+     * character *after* the length. */
+
+    l = strlen(str);
+    return g_bytes_new_take(nm_memdup(str, l + 1u), l);
+}
+
 /**
  * nm_utils_gbytes_equals:
  * @bytes: (allow-none): a #GBytes array to compare. Note that
@@ -434,6 +452,24 @@ nm_g_variant_singleton_u_0(void)
 
 /*****************************************************************************/
 
+GHashTable *
+nm_utils_strdict_clone(GHashTable *src)
+{
+    GHashTable *   dst;
+    GHashTableIter iter;
+    const char *   key;
+    const char *   val;
+
+    if (!src)
+        return NULL;
+
+    dst = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
+    g_hash_table_iter_init(&iter, src);
+    while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
+        g_hash_table_insert(dst, g_strdup(key), g_strdup(val));
+    return dst;
+}
+
 /* Convert a hash table with "char *" keys and values to an "a{ss}" GVariant.
  * The keys will be sorted asciibetically.
  * Returns a floating reference.
@@ -750,27 +786,27 @@ nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
     return 0;
 }
 
-/**
- * _nm_utils_ip4_get_default_prefix:
- * @ip: an IPv4 address (in network byte order)
- *
- * When the Internet was originally set up, various ranges of IP addresses were
- * segmented into three network classes: A, B, and C.  This function will return
- * a prefix that is associated with the IP address specified defining where it
- * falls in the predefined classes.
- *
- * Returns: the default class prefix for the given IP
- **/
-/* The function is originally from ipcalc.c of Red Hat's initscripts. */
+/*****************************************************************************/
+
 guint32
-_nm_utils_ip4_get_default_prefix(guint32 ip)
+_nm_utils_ip4_get_default_prefix0(in_addr_t ip)
 {
-    if (((ntohl(ip) & 0xFF000000) >> 24) <= 127)
-        return 8; /* Class A - 255.0.0.0 */
-    else if (((ntohl(ip) & 0xFF000000) >> 24) <= 191)
-        return 16; /* Class B - 255.255.0.0 */
+    /* The function is originally from ipcalc.c of Red Hat's initscripts. */
+    switch (ntohl(ip) >> 24) {
+    case 0 ... 127:
+        return 8; /* Class A */
+    case 128 ... 191:
+        return 16; /* Class B */
+    case 192 ... 223:
+        return 24; /* Class C */
+    }
+    return 0;
+}
 
-    return 24; /* Class C - 255.255.255.0 */
+guint32
+_nm_utils_ip4_get_default_prefix(in_addr_t ip)
+{
+    return _nm_utils_ip4_get_default_prefix0(ip) ?: 24;
 }
 
 gboolean
@@ -2231,6 +2267,8 @@ _nm_utils_ascii_str_to_bool(const char *str, int default_value)
 
 /*****************************************************************************/
 
+NM_CACHED_QUARK_FCN("nm-manager-error-quark", nm_manager_error_quark);
+
 NM_CACHED_QUARK_FCN("nm-utils-error-quark", nm_utils_error_quark);
 
 void
@@ -3066,6 +3104,18 @@ nm_utils_fd_read_loop_exact(int fd, void *buf, size_t nbytes, bool do_poll)
 
 /*****************************************************************************/
 
+void
+nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val)
+{
+    if (val) {
+        gs_free gpointer x_name  = NULL;
+        gs_free gpointer x_value = NULL;
+
+        x_name  = (gpointer) g_steal_pointer(&val->name);
+        x_value = g_steal_pointer(&val->value_ptr);
+    }
+}
+
 G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, name) == 0);
 
 NMUtilsNamedValue *
@@ -3262,6 +3312,62 @@ nm_utils_hash_values_to_array(GHashTable *     hash,
     return arr;
 }
 
+/*****************************************************************************/
+
+/**
+ * nm_utils_hashtable_equal:
+ * @a: one #GHashTable
+ * @b: other #GHashTable
+ * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is
+ *   treated like an empty hash. It means, a %NULL hash will compare equal
+ *   to an empty hash.
+ * @equal_func: the equality function, for comparing the values.
+ *   If %NULL, the values are not compared. In that case, the function
+ *   only checks, if both dictionaries have the same keys -- according
+ *   to @b's key equality function.
+ *   Note that the values of @a will be passed as first argument
+ *   to @equal_func.
+ *
+ * Compares two hash tables, whether they have equal content.
+ * This only makes sense, if @a and @b have the same key types and
+ * the same key compare-function.
+ *
+ * Returns: %TRUE, if both dictionaries have the same content.
+ */
+gboolean
+nm_utils_hashtable_equal(const GHashTable *a,
+                         const GHashTable *b,
+                         gboolean          treat_null_as_empty,
+                         GEqualFunc        equal_func)
+{
+    guint          n;
+    GHashTableIter iter;
+    gconstpointer  key, v_a, v_b;
+
+    if (a == b)
+        return TRUE;
+    if (!treat_null_as_empty) {
+        if (!a || !b)
+            return FALSE;
+    }
+
+    n = a ? g_hash_table_size((GHashTable *) a) : 0;
+    if (n != (b ? g_hash_table_size((GHashTable *) b) : 0))
+        return FALSE;
+
+    if (n > 0) {
+        g_hash_table_iter_init(&iter, (GHashTable *) a);
+        while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &v_a)) {
+            if (!g_hash_table_lookup_extended((GHashTable *) b, key, NULL, (gpointer *) &v_b))
+                return FALSE;
+            if (equal_func && !equal_func(v_a, v_b))
+                return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
 static gboolean
 _utils_hashtable_equal(GHashTable *     hash_a,
                        GHashTable *     hash_b,
@@ -3301,7 +3407,7 @@ _utils_hashtable_equal(GHashTable *     hash_a,
 }
 
 /**
- * nm_utils_hashtable_equal:
+ * nm_utils_hashtable_cmp_equal:
  * @a: (allow-none): the hash table or %NULL
  * @b: (allow-none): the other hash table or %NULL
  * @cmp_values: (allow-none): if %NULL, only the keys
@@ -3316,10 +3422,10 @@ _utils_hashtable_equal(GHashTable *     hash_a,
  *   @cmp_values is given) all values are the same.
  */
 gboolean
-nm_utils_hashtable_equal(const GHashTable *a,
-                         const GHashTable *b,
-                         GCompareDataFunc  cmp_values,
-                         gpointer          user_data)
+nm_utils_hashtable_cmp_equal(const GHashTable *a,
+                             const GHashTable *b,
+                             GCompareDataFunc  cmp_values,
+                             gpointer          user_data)
 {
     GHashTable *hash_a = (GHashTable *) a;
     GHashTable *hash_b = (GHashTable *) b;
@@ -3374,7 +3480,7 @@ _hashtable_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data)
  * @a: (allow-none): the hash to compare. May be %NULL.
  * @b: (allow-none): the other hash to compare. May be %NULL.
  * @do_fast_precheck: if %TRUE, assume that the hashes are equal
- *   and that it is worth calling nm_utils_hashtable_equal() first.
+ *   and that it is worth calling nm_utils_hashtable_cmp_equal() first.
  *   That requires, that both hashes have the same equals function
  *   which is compatible with the @cmp_keys function.
  * @cmp_keys: the compare function for keys. Usually, the hash/equal function
@@ -3827,62 +3933,6 @@ nm_utils_array_find_binary_search(gconstpointer    list,
 /*****************************************************************************/
 
 /**
- * nm_utils_hash_table_equal:
- * @a: one #GHashTable
- * @b: other #GHashTable
- * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is
- *   treated like an empty hash. It means, a %NULL hash will compare equal
- *   to an empty hash.
- * @equal_func: the equality function, for comparing the values.
- *   If %NULL, the values are not compared. In that case, the function
- *   only checks, if both dictionaries have the same keys -- according
- *   to @b's key equality function.
- *   Note that the values of @a will be passed as first argument
- *   to @equal_func.
- *
- * Compares two hash tables, whether they have equal content.
- * This only makes sense, if @a and @b have the same key types and
- * the same key compare-function.
- *
- * Returns: %TRUE, if both dictionaries have the same content.
- */
-gboolean
-nm_utils_hash_table_equal(const GHashTable *        a,
-                          const GHashTable *        b,
-                          gboolean                  treat_null_as_empty,
-                          NMUtilsHashTableEqualFunc equal_func)
-{
-    guint          n;
-    GHashTableIter iter;
-    gconstpointer  key, v_a, v_b;
-
-    if (a == b)
-        return TRUE;
-    if (!treat_null_as_empty) {
-        if (!a || !b)
-            return FALSE;
-    }
-
-    n = a ? g_hash_table_size((GHashTable *) a) : 0;
-    if (n != (b ? g_hash_table_size((GHashTable *) b) : 0))
-        return FALSE;
-
-    if (n > 0) {
-        g_hash_table_iter_init(&iter, (GHashTable *) a);
-        while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &v_a)) {
-            if (!g_hash_table_lookup_extended((GHashTable *) b, key, NULL, (gpointer *) &v_b))
-                return FALSE;
-            if (equal_func && !equal_func(v_a, v_b))
-                return FALSE;
-        }
-    }
-
-    return TRUE;
-}
-
-/*****************************************************************************/
-
-/**
  * nm_utils_get_start_time_for_pid:
  * @pid: the process identifier
  * @out_state: return the state character, like R, S, Z. See `man 5 proc`.
@@ -4749,7 +4799,7 @@ typedef struct {
     GMainContext *context;
     GHashTable *  fds;
     GPollFD *     fds_arr;
-    int           fds_len;
+    guint         fds_len;
     int           max_priority;
     bool          acquired : 1;
 } CtxIntegSource;
@@ -4792,13 +4842,15 @@ _ctx_integ_source_prepare(GSource *source, int *out_timeout)
     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;
+    GHashTableIter  h_iter;
+    PollData *      poll_data;
+    gboolean        fds_changed;
+    GPollFD         new_fds_stack[300u / sizeof(GPollFD)];
+    gs_free GPollFD *new_fds_heap = NULL;
+    GPollFD *        new_fds;
+    guint            new_fds_len;
+    guint            new_fds_alloc;
+    guint            i;
 
     _CTX_LOG("prepare...");
 
@@ -4806,30 +4858,44 @@ _ctx_integ_source_prepare(GSource *source, int *out_timeout)
 
     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;
+    new_fds_alloc = NM_MAX(G_N_ELEMENTS(new_fds_stack), 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);
+    if (new_fds_alloc > G_N_ELEMENTS(new_fds_stack)) {
+        new_fds_heap = g_new(GPollFD, new_fds_alloc);
+        new_fds      = new_fds_heap;
+    } else
+        new_fds = new_fds_stack;
 
-    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);
+    for (;;) {
+        int l;
+
+        nm_assert(new_fds_alloc <= (guint) G_MAXINT);
+
+        l = g_main_context_query(ctx_src->context,
+                                 max_priority,
+                                 &timeout,
+                                 new_fds,
+                                 (int) new_fds_alloc);
+        nm_assert(l >= 0);
+
+        new_fds_len = (guint) l;
+
+        if (G_LIKELY(new_fds_len <= new_fds_alloc))
+            break;
+
+        new_fds_alloc = new_fds_len;
+        g_free(new_fds_heap);
+        new_fds_heap = g_new(GPollFD, new_fds_alloc);
+        new_fds      = new_fds_heap;
     }
 
     fds_changed = FALSE;
-    if (fds_len_old != ctx_src->fds_len)
+    if (new_fds_len != 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) {
+        for (i = 0; i < new_fds_len; i++) {
+            if (new_fds[i].fd != ctx_src->fds_arr[i].fd
+                || new_fds[i].events != ctx_src->fds_arr[i].events) {
                 fds_changed = TRUE;
                 break;
             }
@@ -4837,6 +4903,13 @@ _ctx_integ_source_prepare(GSource *source, int *out_timeout)
     }
 
     if (G_UNLIKELY(fds_changed)) {
+        g_free(ctx_src->fds_arr);
+        ctx_src->fds_len = new_fds_len;
+        if (G_LIKELY(new_fds == new_fds_stack) || new_fds_alloc != new_fds_len)
+            ctx_src->fds_arr = nm_memdup(new_fds, sizeof(*new_fds) * new_fds_len);
+        else
+            ctx_src->fds_arr = g_steal_pointer(&new_fds_heap);
+
         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;
@@ -4971,10 +5044,12 @@ _ctx_integ_source_check(GSource *source)
             ctx_src->fds_arr[poll_data->idx.one].revents = revents;
     }
 
+    nm_assert(ctx_src->fds_len <= (guint) G_MAXINT);
+
     some_ready = g_main_context_check(ctx_src->context,
                                       ctx_src->max_priority,
                                       ctx_src->fds_arr,
-                                      ctx_src->fds_len);
+                                      (int) ctx_src->fds_len);
 
     _CTX_LOG("check (some-ready=%d)...", some_ready);
 
@@ -5088,6 +5163,31 @@ nm_utils_g_main_context_create_integrate_source(GMainContext *inner_context)
     return &ctx_src->source;
 }
 
+/*****************************************************************************/
+
+void
+nm_utils_ifname_cpy(char *dst, const char *name)
+{
+    int i;
+
+    g_return_if_fail(dst);
+    g_return_if_fail(name && name[0]);
+
+    nm_assert(nm_utils_ifname_valid_kernel(name, NULL));
+
+    /* ensures NUL padding of the entire IFNAMSIZ buffer. */
+
+    for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++)
+        dst[i] = name[i];
+
+    nm_assert(name[i] == '\0');
+
+    for (; i < (int) IFNAMSIZ; i++)
+        dst[i] = '\0';
+}
+
+/*****************************************************************************/
+
 gboolean
 nm_utils_ifname_valid_kernel(const char *name, GError **error)
 {
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index 23884a37..7d330458 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2016 Red Hat, Inc.
  */
@@ -8,6 +8,18 @@
 
 #include <netinet/in.h>
 
+/*****************************************************************************/
+
+/* An optional boolean (like NMTernary, with identical numerical
+ * enum values). Note that this enum type is _nm_packed! */
+typedef enum _nm_packed {
+    NM_OPTION_BOOL_DEFAULT = -1,
+    NM_OPTION_BOOL_FALSE   = 0,
+    NM_OPTION_BOOL_TRUE    = 1,
+} NMOptionBool;
+
+/*****************************************************************************/
+
 static inline gboolean
 nm_is_ascii(char ch)
 {
@@ -88,14 +100,25 @@ typedef struct {
 } NMEtherAddr;
 
 #define NM_ETHER_ADDR_FORMAT_STR "%02X:%02X:%02X:%02X:%02X:%02X"
-#define NM_ETHER_ADDR_FORMAT_VAL(x)                                            \
-    (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], \
-        (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
-#define NM_ETHER_ADDR_INIT(...)            \
-    {                                      \
-        .ether_addr_octet = {__VA_ARGS__}, \
+
+#define NM_ETHER_ADDR_FORMAT_VAL(x)                                               \
+    (x)->ether_addr_octet[0], (x)->ether_addr_octet[1], (x)->ether_addr_octet[2], \
+        (x)->ether_addr_octet[3], (x)->ether_addr_octet[4], (x)->ether_addr_octet[5]
+
+#define _NM_ETHER_ADDR_INIT(a0, a1, a2, a3, a4, a5) \
+    {                                               \
+        .ether_addr_octet = {                       \
+            (a0),                                   \
+            (a1),                                   \
+            (a2),                                   \
+            (a3),                                   \
+            (a4),                                   \
+            (a5),                                   \
+        },                                          \
     }
 
+#define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__))
+
 static inline int
 nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b)
 {
@@ -308,6 +331,8 @@ nm_utils_is_separator(const char c)
 
 GBytes *nm_gbytes_get_empty(void);
 
+GBytes *nm_g_bytes_new_from_str(const char *str);
+
 static inline gboolean
 nm_gbytes_equal0(GBytes *a, GBytes *b)
 {
@@ -318,6 +343,8 @@ gboolean nm_utils_gbytes_equal_mem(GBytes *bytes, gconstpointer mem_data, gsize
 
 GVariant *nm_utils_gbytes_to_variant_ay(GBytes *bytes);
 
+GHashTable *nm_utils_strdict_clone(GHashTable *src);
+
 GVariant *nm_utils_strdict_to_variant_ass(GHashTable *strdict);
 GVariant *nm_utils_strdict_to_variant_asv(GHashTable *strdict);
 
@@ -672,7 +699,8 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free)
 /*****************************************************************************/
 
 guint32 _nm_utils_ip4_prefix_to_netmask(guint32 prefix);
-guint32 _nm_utils_ip4_get_default_prefix(guint32 ip);
+guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip);
+guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip);
 
 gconstpointer
 nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen);
@@ -758,8 +786,10 @@ typedef struct {
     {                                                                           \
         static const NMUtilsFlags2StrDesc descs[] = {__VA_ARGS__};              \
         G_STATIC_ASSERT(sizeof(flags_type) <= sizeof(unsigned));                \
+                                                                                \
         return nm_utils_flags2str(descs, G_N_ELEMENTS(descs), flags, buf, len); \
-    };
+    }                                                                           \
+    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
 
 const char *nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs,
                                gsize                       n_descs,
@@ -796,7 +826,8 @@ case v:                             \
                 g_snprintf(buf, len, "(%" int_fmt ")", val);               \
         }                                                                  \
         return buf;                                                        \
-    }
+    }                                                                      \
+    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
 
 #define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \
     NM_UTILS_ENUM2STR_DEFINE_FULL(fcn_name, lookup_type, "d", __VA_ARGS__)
@@ -918,6 +949,8 @@ _nm_g_slice_free_fcn_define(1) _nm_g_slice_free_fcn_define(2) _nm_g_slice_free_f
  *   error reason. Depending on the usage, this might indicate a bug because
  *   usually the target object should stay alive as long as there are pending
  *   operations.
+ * @NM_UTILS_ERROR_NOT_READY: the failure is related to being currently
+ *   not ready to perform the operation.
  *
  * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular
  *   purpose during nm_device_check_connection_compatible() to indicate that
@@ -938,6 +971,7 @@ typedef enum {
     NM_UTILS_ERROR_UNKNOWN = 0,         /*< nick=Unknown >*/
     NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/
     NM_UTILS_ERROR_INVALID_ARGUMENT,    /*< nick=InvalidArgument >*/
+    NM_UTILS_ERROR_NOT_READY,           /*< nick=NotReady >*/
 
     /* the following codes have a special meaning and are exactly used for
      * nm_device_check_connection_compatible() and nm_device_check_connection_available().
@@ -961,6 +995,12 @@ typedef enum {
 #define NM_UTILS_ERROR (nm_utils_error_quark())
 GQuark nm_utils_error_quark(void);
 
+GQuark nm_manager_error_quark(void);
+#define _NM_MANAGER_ERROR (nm_manager_error_quark())
+
+#define _NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL  10
+#define _NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN 11
+
 void nm_utils_error_set_cancelled(GError **error, gboolean is_disposing, const char *instance_name);
 
 static inline GError *
@@ -1214,6 +1254,27 @@ nm_g_variant_is_of_type(GVariant *value, const GVariantType *type)
     return value && g_variant_is_of_type(value, type);
 }
 
+static inline GVariant *
+nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr)
+{
+    return g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+                                     addr ?: &nm_ip_addr_zero,
+                                     nm_utils_addr_family_to_size(addr_family),
+                                     1);
+}
+
+static inline GVariant *
+nm_g_variant_new_ay_in4addr(in_addr_t addr)
+{
+    return nm_g_variant_new_ay_inaddr(AF_INET, &addr);
+}
+
+static inline GVariant *
+nm_g_variant_new_ay_in6addr(const struct in6_addr *addr)
+{
+    return nm_g_variant_new_ay_inaddr(AF_INET6, addr);
+}
+
 static inline void
 nm_g_variant_builder_add_sv(GVariantBuilder *builder, const char *key, GVariant *val)
 {
@@ -1304,6 +1365,59 @@ nm_g_source_attach(GSource *source, GMainContext *context)
     return source;
 }
 
+static inline GSource *
+nm_g_idle_add_source(GSourceFunc func, gpointer user_data)
+{
+    /* G convenience function to attach a new timeout source to the default GMainContext.
+     * In that sense it's very similar to g_idle_add() except that it returns a
+     * reference to the new source.  */
+    return nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_DEFAULT, func, user_data, NULL),
+                              NULL);
+}
+
+static inline GSource *
+nm_g_timeout_add_source(guint timeout_msec, GSourceFunc func, gpointer user_data)
+{
+    /* G convenience function to attach a new timeout source to the default GMainContext.
+     * In that sense it's very similar to g_timeout_add() except that it returns a
+     * reference to the new source.  */
+    return nm_g_source_attach(
+        nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL),
+        NULL);
+}
+
+static inline GSource *
+nm_g_timeout_add_source_seconds(guint timeout_sec, GSourceFunc func, gpointer user_data)
+{
+    /* G convenience function to attach a new timeout source to the default GMainContext.
+     * In that sense it's very similar to g_timeout_add_seconds() except that it returns a
+     * reference to the new source.  */
+    return nm_g_source_attach(
+        nm_g_timeout_source_new_seconds(timeout_sec, G_PRIORITY_DEFAULT, func, user_data, NULL),
+        NULL);
+}
+
+static inline GSource *
+nm_g_timeout_add_source_approx(guint       timeout_msec,
+                               guint       timeout_sec_threshold,
+                               GSourceFunc func,
+                               gpointer    user_data)
+{
+    GSource *source;
+
+    /* If timeout_msec is larger or equal than a threshold, then we use g_timeout_source_new_seconds()
+     * instead. */
+    if (timeout_msec / 1000u >= timeout_sec_threshold)
+        source = nm_g_timeout_source_new_seconds(timeout_msec / 1000u,
+                                                 G_PRIORITY_DEFAULT,
+                                                 func,
+                                                 user_data,
+                                                 NULL);
+    else
+        source = nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL);
+    return nm_g_source_attach(source, NULL);
+}
+
 NM_AUTO_DEFINE_FCN0(GMainContext *, _nm_auto_unref_gmaincontext, g_main_context_unref);
 #define nm_auto_unref_gmaincontext nm_auto(_nm_auto_unref_gmaincontext)
 
@@ -1434,6 +1548,8 @@ void nm_utils_named_value_list_sort(NMUtilsNamedValue *arr,
                                     GCompareDataFunc   compare_func,
                                     gpointer           user_data);
 
+void nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val);
+
 /*****************************************************************************/
 
 gpointer *nm_utils_hash_keys_to_array(GHashTable *     hash,
@@ -1457,13 +1573,18 @@ nm_utils_strdict_get_keys(const GHashTable *hash, gboolean sorted, guint *out_le
 
 gboolean nm_utils_hashtable_equal(const GHashTable *a,
                                   const GHashTable *b,
-                                  GCompareDataFunc  cmp_values,
-                                  gpointer          user_data);
+                                  gboolean          treat_null_as_empty,
+                                  GEqualFunc        equal_func);
+
+gboolean nm_utils_hashtable_cmp_equal(const GHashTable *a,
+                                      const GHashTable *b,
+                                      GCompareDataFunc  cmp_values,
+                                      gpointer          user_data);
 
 static inline gboolean
 nm_utils_hashtable_same_keys(const GHashTable *a, const GHashTable *b)
 {
-    return nm_utils_hashtable_equal(a, b, NULL, NULL);
+    return nm_utils_hashtable_cmp_equal(a, b, NULL, NULL);
 }
 
 int nm_utils_hashtable_cmp(const GHashTable *a,
@@ -1508,6 +1629,13 @@ nm_g_array_len(const GArray *arr)
     return arr ? arr->len : 0u;
 }
 
+static inline void
+nm_g_array_unref(GArray *arr)
+{
+    if (arr)
+        g_array_unref(arr);
+}
+
 #define nm_g_array_append_new(arr, type)   \
     ({                                     \
         GArray *const _arr = (arr);        \
@@ -1522,6 +1650,55 @@ nm_g_array_len(const GArray *arr)
 
 /*****************************************************************************/
 
+static inline GPtrArray *
+nm_g_ptr_array_ref(GPtrArray *arr)
+{
+    return arr ? g_ptr_array_ref(arr) : NULL;
+}
+
+static inline void
+nm_g_ptr_array_unref(GPtrArray *arr)
+{
+    if (arr)
+        g_ptr_array_unref(arr);
+}
+
+#define nm_g_ptr_array_set(pdst, val)                              \
+    ({                                                             \
+        GPtrArray **_pdst    = (pdst);                             \
+        GPtrArray * _val     = (val);                              \
+        gboolean    _changed = FALSE;                              \
+                                                                   \
+        nm_assert(_pdst);                                          \
+                                                                   \
+        if (*_pdst != _val) {                                      \
+            _nm_unused gs_unref_ptrarray GPtrArray *_old = *_pdst; \
+                                                                   \
+            *_pdst   = nm_g_ptr_array_ref(_val);                   \
+            _changed = TRUE;                                       \
+        }                                                          \
+        _changed;                                                  \
+    })
+
+#define nm_g_ptr_array_set_take(pdst, val)                         \
+    ({                                                             \
+        GPtrArray **_pdst    = (pdst);                             \
+        GPtrArray * _val     = (val);                              \
+        gboolean    _changed = FALSE;                              \
+                                                                   \
+        nm_assert(_pdst);                                          \
+                                                                   \
+        if (*_pdst != _val) {                                      \
+            _nm_unused gs_unref_ptrarray GPtrArray *_old = *_pdst; \
+                                                                   \
+            *_pdst   = _val;                                       \
+            _changed = TRUE;                                       \
+        } else {                                                   \
+            nm_g_ptr_array_unref(_val);                            \
+        }                                                          \
+        _changed;                                                  \
+    })
+
 static inline guint
 nm_g_ptr_array_len(const GPtrArray *arr)
 {
@@ -1571,6 +1748,19 @@ GPtrArray *_nm_g_ptr_array_copy(GPtrArray *    array,
 
 /*****************************************************************************/
 
+static inline GHashTable *
+nm_g_hash_table_ref(GHashTable *hash)
+{
+    return hash ? g_hash_table_ref(hash) : NULL;
+}
+
+static inline void
+nm_g_hash_table_unref(GHashTable *hash)
+{
+    if (hash)
+        g_hash_table_unref(hash);
+}
+
 static inline guint
 nm_g_hash_table_size(GHashTable *hash)
 {
@@ -1614,27 +1804,16 @@ gssize nm_utils_array_find_binary_search(gconstpointer    list,
 
 /*****************************************************************************/
 
-typedef gboolean (*NMUtilsHashTableEqualFunc)(gconstpointer a, gconstpointer b);
-
-gboolean nm_utils_hash_table_equal(const GHashTable *        a,
-                                   const GHashTable *        b,
-                                   gboolean                  treat_null_as_empty,
-                                   NMUtilsHashTableEqualFunc equal_func);
-
-/*****************************************************************************/
-
 void _nm_utils_strv_sort(const char **strv, gssize len);
 #define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort(NM_CAST_STRV_MC(strv), len)
 
 int
 _nm_utils_strv_cmp_n(const char *const *strv1, gssize len1, const char *const *strv2, gssize len2);
 
-static inline gboolean
-_nm_utils_strv_equal(char **strv1, char **strv2)
-{
-    return _nm_utils_strv_cmp_n((const char *const *) strv1, -1, (const char *const *) strv2, -1)
-           == 0;
-}
+#define nm_utils_strv_cmp_n(strv1, len1, strv2, len2) \
+    _nm_utils_strv_cmp_n(NM_CAST_STRV_CC(strv1), (len1), NM_CAST_STRV_CC(strv2), (len2))
+
+#define nm_utils_strv_equal(strv1, strv2) (nm_utils_strv_cmp_n((strv1), -1, (strv2), -1) == 0)
 
 /*****************************************************************************/
 
@@ -1851,14 +2030,14 @@ nm_strv_ptrarray_contains(const GPtrArray *strv, const char *str)
 static inline int
 nm_strv_ptrarray_cmp(const GPtrArray *a, const GPtrArray *b)
 {
-    /* _nm_utils_strv_cmp_n() will treat NULL and empty arrays the same.
+    /* nm_utils_strv_cmp_n() will treat NULL and empty arrays the same.
      * That means, an empty strv array can both be represented by NULL
      * and an array of length zero.
      * If you need to distinguish between these case, do that yourself. */
-    return _nm_utils_strv_cmp_n((const char *const *) nm_g_ptr_array_pdata(a),
-                                nm_g_ptr_array_len(a),
-                                (const char *const *) nm_g_ptr_array_pdata(b),
-                                nm_g_ptr_array_len(b));
+    return nm_utils_strv_cmp_n((const char *const *) nm_g_ptr_array_pdata(a),
+                               nm_g_ptr_array_len(a),
+                               (const char *const *) nm_g_ptr_array_pdata(b),
+                               nm_g_ptr_array_len(b));
 }
 
 /*****************************************************************************/
@@ -1985,6 +2164,24 @@ _nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize buffer_length, gsi
                                     out_length);
 }
 
+static inline guint8 *
+_nm_utils_hwaddr_aton_exact(const char *asc, gpointer buffer, gsize buffer_length)
+{
+    g_return_val_if_fail(asc, NULL);
+    g_return_val_if_fail(buffer, NULL);
+    g_return_val_if_fail(buffer_length > 0, NULL);
+
+    return nm_utils_hexstr2bin_full(asc,
+                                    FALSE,
+                                    TRUE,
+                                    FALSE,
+                                    ":-",
+                                    buffer_length,
+                                    buffer,
+                                    buffer_length,
+                                    NULL);
+}
+
 static inline const char *
 _nm_utils_hwaddr_ntoa(gconstpointer addr,
                       gsize         addr_len,
@@ -2060,7 +2257,8 @@ _nm_utils_hwaddr_ntoa(gconstpointer addr,
         {                                                                                      \
             unknown_val_cmd;                                                                   \
         }                                                                                      \
-    }
+    }                                                                                          \
+    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
 
 #define NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE(fcn_name,        \
                                                    result_type,     \
@@ -2130,10 +2328,31 @@ nm_utils_strdup_reset(char **dst, const char *src)
     return TRUE;
 }
 
+static inline gboolean
+nm_utils_strdup_reset_take(char **dst, char *src)
+{
+    char *old;
+
+    nm_assert(dst);
+    nm_assert(src != *dst);
+
+    if (nm_streq0(*dst, src)) {
+        if (src)
+            g_free(src);
+        return FALSE;
+    }
+    old  = *dst;
+    *dst = src;
+    g_free(old);
+    return TRUE;
+}
+
 void nm_indirect_g_free(gpointer arg);
 
 /*****************************************************************************/
 
+void nm_utils_ifname_cpy(char *dst, const char *name);
+
 typedef enum {
     NMU_IFACE_ANY,
     NMU_IFACE_KERNEL,
diff --git a/shared/nm-glib-aux/nm-str-buf.h b/shared/nm-glib-aux/nm-str-buf.h
index 062630a5..cb0d3fb1 100644
--- a/shared/nm-glib-aux/nm-str-buf.h
+++ b/shared/nm-glib-aux/nm-str-buf.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #ifndef __NM_STR_BUF_H__
 #define __NM_STR_BUF_H__
@@ -31,7 +31,7 @@ typedef struct _NMStrBuf {
 /*****************************************************************************/
 
 static inline void
-_nm_str_buf_assert(NMStrBuf *strbuf)
+_nm_str_buf_assert(const NMStrBuf *strbuf)
 {
     nm_assert(strbuf);
     nm_assert((!!strbuf->_priv_str) == (strbuf->_priv_allocated > 0));
@@ -268,7 +268,34 @@ nm_str_buf_append_required_delimiter(NMStrBuf *strbuf, char delimiter)
 }
 
 static inline void
-nm_str_buf_reset(NMStrBuf *strbuf, const char *str)
+nm_str_buf_append_dirty(NMStrBuf *strbuf, gsize len)
+{
+    _nm_str_buf_assert(strbuf);
+
+    /* this append @len bytes to the buffer, but it does not
+     * initialize them! */
+    if (len > 0) {
+        nm_str_buf_maybe_expand(strbuf, len, FALSE);
+        strbuf->_priv_len += len;
+    }
+}
+
+static inline void
+nm_str_buf_append_c_len(NMStrBuf *strbuf, char ch, gsize len)
+{
+    _nm_str_buf_assert(strbuf);
+
+    if (len > 0) {
+        nm_str_buf_maybe_expand(strbuf, len, FALSE);
+        memset(&strbuf->_priv_str[strbuf->_priv_len], ch, len);
+        strbuf->_priv_len += len;
+    }
+}
+
+/*****************************************************************************/
+
+static inline NMStrBuf *
+nm_str_buf_reset(NMStrBuf *strbuf)
 {
     _nm_str_buf_assert(strbuf);
 
@@ -280,8 +307,7 @@ nm_str_buf_reset(NMStrBuf *strbuf, const char *str)
         strbuf->_priv_len = 0;
     }
 
-    if (str)
-        nm_str_buf_append(strbuf, str);
+    return strbuf;
 }
 
 /*****************************************************************************/
@@ -361,6 +387,31 @@ nm_str_buf_get_str_unsafe(NMStrBuf *strbuf)
     return strbuf->_priv_str;
 }
 
+static inline char *
+nm_str_buf_get_str_at_unsafe(NMStrBuf *strbuf, gsize index)
+{
+    _nm_str_buf_assert(strbuf);
+
+    /* it is acceptable to ask for a pointer at the end of the buffer -- even
+     * if there is no data there. The caller is anyway required to take care
+     * of the length (that's the "unsafe" part), and in that case, the length
+     * is merely zero. */
+    nm_assert(index <= strbuf->allocated);
+
+    if (!strbuf->_priv_str)
+        return NULL;
+
+    return &strbuf->_priv_str[index];
+}
+
+static inline char
+nm_str_buf_get_char(const NMStrBuf *strbuf, gsize index)
+{
+    _nm_str_buf_assert(strbuf);
+    nm_assert(index < strbuf->allocated);
+    return strbuf->_priv_str[index];
+}
+
 /**
  * nm_str_buf_finalize:
  * @strbuf: an initilized #NMStrBuf
diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c
index 511363c3..df98176a 100644
--- a/shared/nm-glib-aux/nm-time-utils.c
+++ b/shared/nm-glib-aux/nm-time-utils.c
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-time-utils.h"
 
diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h
index 77aeae5b..3c3e935f 100644
--- a/shared/nm-glib-aux/nm-time-utils.h
+++ b/shared/nm-glib-aux/nm-time-utils.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/nm-value-type.h b/shared/nm-glib-aux/nm-value-type.h
index c07f1104..f9edebdb 100644
--- a/shared/nm-glib-aux/nm-value-type.h
+++ b/shared/nm-glib-aux/nm-value-type.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2019 Red Hat, Inc.
  */
diff --git a/shared/nm-glib-aux/tests/meson.build b/shared/nm-glib-aux/tests/meson.build
index 8136eff9..464f4e7c 100644
--- a/shared/nm-glib-aux/tests/meson.build
+++ b/shared/nm-glib-aux/tests/meson.build
@@ -1,13 +1,12 @@
-# SPDX-License-Identifier: LGPL-2.1+
+# SPDX-License-Identifier: LGPL-2.1-or-later
 
 exe = executable(
   'test-shared-general',
   'test-shared-general.c',
   c_args: [
-    '-DNETWORKMANAGER_COMPILATION_TEST',
-    '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)',
+    '-DG_LOG_DOMAIN="test"',
   ],
-  dependencies: libnm_utils_base_dep,
+  dependencies: libnm_glib_aux_dep,
   link_with: libnm_systemd_logging_stub,
 )
 
@@ -23,11 +22,10 @@ if jansson_dep.found()
     'test-json-aux',
     'test-json-aux.c',
     c_args: [
-      '-DNETWORKMANAGER_COMPILATION_TEST',
-      '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)',
+      '-DG_LOG_DOMAIN="test"',
     ],
     dependencies: [
-      libnm_utils_base_dep,
+      libnm_glib_aux_dep,
       jansson_dep,
       dl_dep,
     ],
diff --git a/shared/nm-glib-aux/tests/test-json-aux.c b/shared/nm-glib-aux/tests/test-json-aux.c
index 5331c015..b07d673f 100644
--- a/shared/nm-glib-aux/tests/test-json-aux.c
+++ b/shared/nm-glib-aux/tests/test-json-aux.c
@@ -1,8 +1,6 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#define NM_TEST_UTILS_NO_LIBNM 1
-
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-prog.h"
 
 #include <jansson.h>
 
diff --git a/shared/nm-glib-aux/tests/test-shared-general.c b/shared/nm-glib-aux/tests/test-shared-general.c
index 84e0a6f0..f42c6fb1 100644
--- a/shared/nm-glib-aux/tests/test-shared-general.c
+++ b/shared/nm-glib-aux/tests/test-shared-general.c
@@ -1,11 +1,9 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
-#define NM_TEST_UTILS_NO_LIBNM 1
-
-#include "nm-default.h"
+#include "nm-glib-aux/nm-default-glib-i18n-prog.h"
 
 #include "nm-std-aux/unaligned.h"
 #include "nm-glib-aux/nm-random-utils.h"
@@ -364,8 +362,8 @@ test_strv_cmp(void)
         _strv_cmp_fuzz_input((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x);            \
         _strv_cmp_fuzz_input((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x);            \
                                                                                                     \
-        _c1 = _nm_utils_strv_cmp_n(_a1, _l1, _a2, _l2);                                             \
-        _c2 = _nm_utils_strv_cmp_n(_a2, _l2, _a1, _l1);                                             \
+        _c1 = nm_utils_strv_cmp_n(_a1, _l1, _a2, _l2);                                              \
+        _c2 = nm_utils_strv_cmp_n(_a2, _l2, _a1, _l1);                                              \
         if (equal) {                                                                                \
             g_assert_cmpint(_c1, ==, 0);                                                            \
             g_assert_cmpint(_c2, ==, 0);                                                            \
@@ -376,8 +374,8 @@ test_strv_cmp(void)
                                                                                                     \
         /* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x).
          * Either way, the arrays must compare equal to their semantically equal alternative. */ \
-        g_assert_cmpint(_nm_utils_strv_cmp_n(_a1, _l1, _a1x, _l1), ==, 0);                          \
-        g_assert_cmpint(_nm_utils_strv_cmp_n(_a2, _l2, _a2x, _l2), ==, 0);                          \
+        g_assert_cmpint(nm_utils_strv_cmp_n(_a1, _l1, _a1x, _l1), ==, 0);                           \
+        g_assert_cmpint(nm_utils_strv_cmp_n(_a2, _l2, _a2x, _l2), ==, 0);                           \
                                                                                                     \
         _strv_cmp_free_deep(_a1_free_deep, _l1);                                                    \
         _strv_cmp_free_deep(_a2_free_deep, _l2);                                                    \
@@ -632,9 +630,10 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
     {"0", 0},
     {"1", 1},
     {"2", 2},
-    {"3", 3}, )
+    {"3", 3}, );
 
-    static void test_string_table_lookup(void)
+static void
+test_string_table_lookup(void)
 {
     const char *const args[] = {
         NULL,
@@ -762,25 +761,44 @@ test_nm_utils_get_next_realloc_size(void)
         }
 
         {
-            const gsize requested      = requested0;
-            const gsize reserved_true  = nm_utils_get_next_realloc_size(TRUE, requested);
-            const gsize reserved_false = nm_utils_get_next_realloc_size(FALSE, requested);
+            const gsize requested = requested0;
+            gsize       reserved_true;
+            gsize       reserved_false;
+            bool        truncated_true  = FALSE;
+            bool        truncated_false = FALSE;
+
+            if (sizeof(gsize) > 4 && requested > SIZE_MAX / 2u - 24u) {
+                reserved_false  = G_MAXSSIZE;
+                truncated_false = TRUE;
+            } else
+                reserved_false = nm_utils_get_next_realloc_size(FALSE, requested);
+
+            if (sizeof(gsize) > 4 && requested > SIZE_MAX - 0x1000u - 24u) {
+                reserved_true  = G_MAXSSIZE;
+                truncated_true = TRUE;
+            } else
+                reserved_true = nm_utils_get_next_realloc_size(TRUE, requested);
 
             g_assert_cmpuint(reserved_true, >, 0);
             g_assert_cmpuint(reserved_false, >, 0);
-            g_assert_cmpuint(reserved_true, >=, requested);
-            g_assert_cmpuint(reserved_false, >=, requested);
-            g_assert_cmpuint(reserved_false, >=, reserved_true);
+            if (!truncated_true)
+                g_assert_cmpuint(reserved_true, >=, requested);
+            if (!truncated_false)
+                g_assert_cmpuint(reserved_false, >=, requested);
+            if (!truncated_true && !truncated_false)
+                g_assert_cmpuint(reserved_false, >=, reserved_true);
 
             if (i < G_N_ELEMENTS(test_data)) {
-                g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true);
-                g_assert_cmpuint(reserved_false, ==, test_data[i].reserved_false);
+                if (!truncated_true)
+                    g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true);
+                if (!truncated_false)
+                    g_assert_cmpuint(reserved_false, ==, test_data[i].reserved_false);
             }
 
             /* reserved_false is generally the next power of two - 24. */
             if (reserved_false == G_MAXSIZE)
                 g_assert_cmpuint(requested, >, G_MAXSIZE / 2u - 24u);
-            else {
+            else if (!reserved_false) {
                 g_assert_cmpuint(reserved_false, <=, G_MAXSIZE - 24u);
                 if (reserved_false >= 40) {
                     const gsize _pow2 = reserved_false + 24u;
@@ -800,7 +818,7 @@ test_nm_utils_get_next_realloc_size(void)
             /* reserved_true is generally the next 4k border - 24. */
             if (reserved_true == G_MAXSIZE)
                 g_assert_cmpuint(requested, >, G_MAXSIZE - 0x1000u - 24u);
-            else {
+            else if (!truncated_true) {
                 g_assert_cmpuint(reserved_true, <=, G_MAXSIZE - 24u);
                 if (reserved_true > 8168u) {
                     const gsize page_border = reserved_true + 24u;
@@ -951,10 +969,10 @@ again:
         else
             g_assert(!data);
 
-        g_assert(_nm_utils_strv_cmp_n((const char *const *) strv->pdata,
-                                      strv->len,
-                                      (const char *const *) strv2->pdata,
-                                      strv2->len)
+        g_assert(nm_utils_strv_cmp_n((const char *const *) strv->pdata,
+                                     strv->len,
+                                     (const char *const *) strv2->pdata,
+                                     strv2->len)
                  == 0);
     }
 }
@@ -1050,7 +1068,7 @@ test_strv_dup_packed(void)
             g_assert(strv_cpy);
         g_assert(NM_PTRARRAY_LEN(strv_cpy) == strv_len);
         if (strv_cpy)
-            g_assert(_nm_utils_strv_equal((char **) strv_cpy, (char **) strv_src));
+            g_assert(nm_utils_strv_equal(strv_cpy, strv_src));
     }
 }
 
@@ -1189,8 +1207,8 @@ test_utils_hashtable_cmp(void)
             }
 
             g_assert(nm_utils_hashtable_same_keys(h1, h2));
-            g_assert(nm_utils_hashtable_equal(h1, h2, NULL, NULL));
-            g_assert(nm_utils_hashtable_equal(h1, h2, func_val_cmp, NULL));
+            g_assert(nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL));
+            g_assert(nm_utils_hashtable_cmp_equal(h1, h2, func_val_cmp, NULL));
             g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0);
             g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0);
             g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) == 0);
@@ -1220,16 +1238,16 @@ again:
 
             if (has_same_keys) {
                 g_assert(nm_utils_hashtable_same_keys(h1, h2));
-                g_assert(nm_utils_hashtable_equal(h1, h2, NULL, NULL));
+                g_assert(nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL));
                 g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0);
                 g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0);
             } else {
                 g_assert(!nm_utils_hashtable_same_keys(h1, h2));
-                g_assert(!nm_utils_hashtable_equal(h1, h2, NULL, NULL));
+                g_assert(!nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL));
                 g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) != 0);
                 g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) != 0);
             }
-            g_assert(!nm_utils_hashtable_equal(h1, h2, func_val_cmp, NULL));
+            g_assert(!nm_utils_hashtable_cmp_equal(h1, h2, func_val_cmp, NULL));
             g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) != 0);
             g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, func_val_cmp, NULL) != 0);
         }