[NetworkManager-libreswan/fg/GUI_IKEv2_advanced_options: 1/7] shared: update shared files



commit fa8d03185ba48bcb0a2bdc114d9a76cdcb7c274a
Author: Francesco Giudici <fgiudici redhat com>
Date:   Mon Sep 10 17:51:06 2018 +0200

    shared: update shared files

 Makefile.am                           |    3 +-
 shared/nm-default.h                   |    2 -
 shared/nm-utils/gsystem-local-alloc.h |  208 ----
 shared/nm-utils/nm-glib.h             |  112 ++-
 shared/nm-utils/nm-macros-internal.h  |  880 ++++++++++++++---
 shared/nm-utils/nm-shared-utils.c     | 1717 ++++++++++++++++++++++++++++++++-
 shared/nm-utils/nm-shared-utils.h     |  747 +++++++++++++-
 7 files changed, 3321 insertions(+), 348 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index ed86802..8442d64 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -60,6 +60,8 @@ properties_libutils_la_SOURCES = \
        shared/utils.h \
        shared/nm-utils/nm-vpn-plugin-utils.c \
        shared/nm-utils/nm-vpn-plugin-utils.h \
+       shared/nm-utils/nm-shared-utils.c \
+       shared/nm-utils/nm-shared-utils.h \
        shared/nm-service-defines.h
 
 properties_libutils_la_CPPFLAGS = \
@@ -286,7 +288,6 @@ EXTRA_DIST += \
 
 EXTRA_DIST += \
        shared/README \
-       shared/nm-utils/gsystem-local-alloc.h \
        shared/nm-utils/nm-glib.h \
        shared/nm-utils/nm-macros-internal.h \
        shared/nm-utils/nm-shared-utils.c \
diff --git a/shared/nm-default.h b/shared/nm-default.h
index 8dfb6af..58807cc 100644
--- a/shared/nm-default.h
+++ b/shared/nm-default.h
@@ -42,8 +42,6 @@
 
 /* always include these headers for our internal source files. */
 
-#include "nm-utils/nm-glib.h"
-#include "nm-utils/gsystem-local-alloc.h"
 #include "nm-utils/nm-macros-internal.h"
 
 #include "nm-version.h"
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index dd18756..770cf0f 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -14,17 +14,19 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_GLIB_H__
 #define __NM_GLIB_H__
 
+/*****************************************************************************/
 
-#include <gio/gio.h>
-#include <string.h>
+#ifndef __NM_MACROS_INTERNAL_H__
+#error "nm-glib.h requires nm-macros-internal.h. Do not include this directly"
+#endif
 
-#include "gsystem-local-alloc.h"
+/*****************************************************************************/
 
 #ifdef __clang__
 
@@ -40,6 +42,8 @@
 
 #endif
 
+/*****************************************************************************/
+
 static inline void
 __g_type_ensure (GType type)
 {
@@ -54,6 +58,8 @@ __g_type_ensure (GType type)
 }
 #define g_type_ensure __g_type_ensure
 
+/*****************************************************************************/
+
 #if !GLIB_CHECK_VERSION(2,34,0)
 
 #define g_clear_pointer(pp, destroy) \
@@ -73,6 +79,12 @@ __g_type_ensure (GType type)
         }                                                                          \
     } G_STMT_END
 
+#endif
+
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION(2,34,0)
+
 /* These are used to clean up the output of test programs; we can just let
  * them no-op in older glib.
  */
@@ -102,6 +114,7 @@ __g_type_ensure (GType type)
 
 #endif
 
+/*****************************************************************************/
 
 #if GLIB_CHECK_VERSION (2, 35, 0)
 /* For glib >= 2.36, g_type_init() is deprecated.
@@ -112,12 +125,15 @@ __g_type_ensure (GType type)
 #define nm_g_type_init()     G_STMT_START { g_type_init (); } G_STMT_END
 #endif
 
+/*****************************************************************************/
 
 /* g_test_initialized() is only available since glib 2.36. */
 #if !GLIB_CHECK_VERSION (2, 36, 0)
 #define g_test_initialized() (g_test_config_vars->test_initialized)
 #endif
 
+/*****************************************************************************/
+
 /* g_assert_cmpmem() is only available since glib 2.46. */
 #if !GLIB_CHECK_VERSION (2, 45, 7)
 #define g_assert_cmpmem(m1, l1, m2, l2) G_STMT_START {\
@@ -132,6 +148,8 @@ __g_type_ensure (GType type)
                                         } G_STMT_END
 #endif
 
+/*****************************************************************************/
+
 /* Rumtime check for glib version. First do a compile time check which
  * (if satisfied) shortcuts the runtime check. */
 static inline gboolean
@@ -146,9 +164,11 @@ nm_glib_check_version (guint major, guint minor, guint micro)
                       && glib_micro_version < micro));
 }
 
+/*****************************************************************************/
+
 /* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */
 static inline void
-__nmtst_g_test_skip (const gchar *msg)
+__nmtst_g_test_skip (const char *msg)
 {
 #if GLIB_CHECK_VERSION (2, 38, 0)
        G_GNUC_BEGIN_IGNORE_DEPRECATIONS
@@ -160,6 +180,7 @@ __nmtst_g_test_skip (const gchar *msg)
 }
 #define g_test_skip __nmtst_g_test_skip
 
+/*****************************************************************************/
 
 /* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */
 static inline void
@@ -184,6 +205,7 @@ __g_test_add_data_func_full (const char     *testpath,
 }
 #define g_test_add_data_func_full __g_test_add_data_func_full
 
+/*****************************************************************************/
 
 #if !GLIB_CHECK_VERSION (2, 34, 0)
 #define G_DEFINE_QUARK(QN, q_n)               \
@@ -199,6 +221,7 @@ q_n##_quark (void)                            \
 }
 #endif
 
+/*****************************************************************************/
 
 static inline gboolean
 nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value)
@@ -245,19 +268,21 @@ nm_g_hash_table_add (GHashTable *hash, gpointer key)
 #endif
 }
 
+/*****************************************************************************/
+
 #if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
 static inline void
 _nm_g_ptr_array_insert (GPtrArray *array,
-                        gint       index_,
+                        int        index_,
                         gpointer   data)
 {
        g_return_if_fail (array);
        g_return_if_fail (index_ >= -1);
-       g_return_if_fail (index_ <= (gint) array->len);
+       g_return_if_fail (index_ <= (int) array->len);
 
        g_ptr_array_add (array, data);
 
-       if (index_ != -1 && index_ != (gint) (array->len - 1)) {
+       if (index_ != -1 && index_ != (int) (array->len - 1)) {
                memmove (&(array->pdata[index_ + 1]),
                         &(array->pdata[index_]),
                         (array->len - index_ - 1) * sizeof (gpointer));
@@ -265,6 +290,7 @@ _nm_g_ptr_array_insert (GPtrArray *array,
        }
 }
 #endif
+
 #if !GLIB_CHECK_VERSION(2, 40, 0)
 #define g_ptr_array_insert(array, index, data) G_STMT_START { _nm_g_ptr_array_insert (array, index, data); } 
G_STMT_END
 #else
@@ -276,14 +302,15 @@ _nm_g_ptr_array_insert (GPtrArray *array,
        } G_STMT_END
 #endif
 
+/*****************************************************************************/
 
 #if !GLIB_CHECK_VERSION (2, 40, 0)
 static inline gboolean
 _g_key_file_save_to_file (GKeyFile     *key_file,
-                          const gchar  *filename,
+                          const char   *filename,
                           GError      **error)
 {
-       gchar *contents;
+       char *contents;
        gboolean success;
        gsize length;
 
@@ -313,6 +340,7 @@ _g_key_file_save_to_file (GKeyFile     *key_file,
        })
 #endif
 
+/*****************************************************************************/
 
 #if GLIB_CHECK_VERSION (2, 36, 0)
 #define g_credentials_get_unix_pid(creds, error) \
@@ -332,6 +360,7 @@ _g_key_file_save_to_file (GKeyFile     *key_file,
        })
 #endif
 
+/*****************************************************************************/
 
 #if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
 static inline gpointer *
@@ -372,6 +401,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table,
        })
 #endif
 
+/*****************************************************************************/
+
 #ifndef g_info
 /* g_info was only added with 2.39.2 */
 #define g_info(...)     g_log (G_LOG_DOMAIN,         \
@@ -379,6 +410,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table,
                                __VA_ARGS__)
 #endif
 
+/*****************************************************************************/
+
 #if !GLIB_CHECK_VERSION(2, 44, 0)
 static inline gpointer
 g_steal_pointer (gpointer pp)
@@ -397,10 +430,11 @@ g_steal_pointer (gpointer pp)
   (0 ? (*(pp)) : (g_steal_pointer) (pp))
 #endif
 
+/*****************************************************************************/
 
 static inline gboolean
-_nm_g_strv_contains (const gchar * const *strv,
-                     const gchar         *str)
+_nm_g_strv_contains (const char * const *strv,
+                     const char          *str)
 {
 #if !GLIB_CHECK_VERSION(2, 44, 0)
        g_return_val_if_fail (strv != NULL, FALSE);
@@ -420,8 +454,10 @@ _nm_g_strv_contains (const gchar * const *strv,
 }
 #define g_strv_contains _nm_g_strv_contains
 
+/*****************************************************************************/
+
 static inline GVariant *
-_nm_g_variant_new_take_string (gchar *string)
+_nm_g_variant_new_take_string (char *string)
 {
 #if !GLIB_CHECK_VERSION(2, 36, 0)
        GVariant *value;
@@ -452,4 +488,54 @@ _nm_g_variant_new_take_string (gchar *string)
 }
 #define g_variant_new_take_string _nm_g_variant_new_take_string
 
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION(2, 38, 0)
+_nm_printf (1, 2)
+static inline GVariant *
+_nm_g_variant_new_printf (const char *format_string, ...)
+{
+       char *string;
+       va_list ap;
+
+       g_return_val_if_fail (format_string, NULL);
+
+       va_start (ap, format_string);
+       string = g_strdup_vprintf (format_string, ap);
+       va_end (ap);
+
+       return g_variant_new_take_string (string);
+}
+#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__)
+#else
+#define g_variant_new_printf(...) \
+       ({ \
+               GVariant *_v; \
+               \
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+               _v = g_variant_new_printf (__VA_ARGS__); \
+               G_GNUC_END_IGNORE_DEPRECATIONS \
+               _v; \
+       })
+#endif
+
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION (2, 56, 0)
+#define g_object_ref(Obj)      ((typeof(Obj)) g_object_ref (Obj))
+#define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink (Obj))
+#endif
+
+/*****************************************************************************/
+
+#ifndef g_autofree
+/* we still don't rely on recent glib to provide g_autofree. Hence, we continue
+ * to use our gs_* free macros that we took from libgsystem.
+ *
+ * To ease migration towards g_auto*, add a compat define for g_autofree. */
+#define g_autofree gs_free
+#endif
+
+/*****************************************************************************/
+
 #endif  /* __NM_GLIB_H__ */
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 2548665..084219b 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -16,6 +16,7 @@
  * 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.
  */
 
@@ -25,58 +26,247 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <string.h>
 
-#include "nm-glib.h"
+#include <gio/gio.h>
+
+/*****************************************************************************/
+
+#define _nm_packed           __attribute__ ((packed))
+#define _nm_unused           __attribute__ ((unused))
+#define _nm_pure             __attribute__ ((pure))
+#define _nm_const            __attribute__ ((const))
+#define _nm_printf(a,b)      __attribute__ ((__format__ (__printf__, a, b)))
+#define _nm_align(s)         __attribute__ ((aligned (s)))
+#define _nm_alignof(type)    __alignof (type)
+#define _nm_alignas(type)    _nm_align (_nm_alignof (type))
+#define nm_auto(fcn)         __attribute__ ((cleanup(fcn)))
+
+
+#if __GNUC__ >= 7
+#define _nm_fallthrough      __attribute__ ((fallthrough))
+#else
+#define _nm_fallthrough
+#endif
+
+/*****************************************************************************/
+
+#ifdef thread_local
+#define _nm_thread_local thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#elif __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && 
__GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
+#define _nm_thread_local _Thread_local
+#else
+#define _nm_thread_local __thread
+#endif
+
+/*****************************************************************************/
+
+#define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \
+static inline void name (void *v) \
+{ \
+       func (*((CastType *) v)); \
+}
+
+#define NM_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \
+static inline void name (void *v) \
+{ \
+       if (*((CastType *) v)) \
+               func (*((CastType *) v)); \
+}
+
+#define NM_AUTO_DEFINE_FCN(Type, name, func) \
+static inline void name (Type *v) \
+{ \
+       func (*v); \
+}
+
+#define NM_AUTO_DEFINE_FCN0(Type, name, func) \
+static inline void name (Type *v) \
+{ \
+       if (*v) \
+               func (*v); \
+}
+
+/*****************************************************************************/
+
+/**
+ * gs_free:
+ *
+ * Call g_free() on a variable location when it goes out of scope.
+ */
+#define gs_free nm_auto(gs_local_free)
+NM_AUTO_DEFINE_FCN_VOID (void *, gs_local_free, g_free)
+
+/**
+ * gs_unref_object:
+ *
+ * Call g_object_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_object_unref(), the variable may be
+ * %NULL.
+ */
+#define gs_unref_object nm_auto(gs_local_obj_unref)
+NM_AUTO_DEFINE_FCN_VOID0 (GObject *, gs_local_obj_unref, g_object_unref)
+
+/**
+ * gs_unref_variant:
+ *
+ * Call g_variant_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_variant_unref(), the variable may be
+ * %NULL.
+ */
+#define gs_unref_variant nm_auto(gs_local_variant_unref)
+NM_AUTO_DEFINE_FCN0 (GVariant *, gs_local_variant_unref, g_variant_unref)
+
+/**
+ * gs_unref_array:
+ *
+ * Call g_array_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define gs_unref_array nm_auto(gs_local_array_unref)
+NM_AUTO_DEFINE_FCN0 (GArray *, gs_local_array_unref, g_array_unref)
+
+/**
+ * gs_unref_ptrarray:
+ *
+ * Call g_ptr_array_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_ptr_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define gs_unref_ptrarray nm_auto(gs_local_ptrarray_unref)
+NM_AUTO_DEFINE_FCN0 (GPtrArray *, gs_local_ptrarray_unref, g_ptr_array_unref)
+
+/**
+ * gs_unref_hashtable:
+ *
+ * Call g_hash_table_unref() on a variable location when it goes out
+ * of scope.  Note that unlike g_hash_table_unref(), the variable may
+ * be %NULL.
+ */
+#define gs_unref_hashtable nm_auto(gs_local_hashtable_unref)
+NM_AUTO_DEFINE_FCN0 (GHashTable *, gs_local_hashtable_unref, g_hash_table_unref)
+
+/**
+ * gs_free_slist:
+ *
+ * Call g_slist_free() on a variable location when it goes out
+ * of scope.
+ */
+#define gs_free_slist nm_auto(gs_local_free_slist)
+NM_AUTO_DEFINE_FCN (GSList *, gs_local_free_slist, g_slist_free)
+
+/**
+ * gs_unref_bytes:
+ *
+ * Call g_bytes_unref() on a variable location when it goes out
+ * of scope.  Note that unlike g_bytes_unref(), the variable may
+ * be %NULL.
+ */
+#define gs_unref_bytes nm_auto(gs_local_bytes_unref)
+NM_AUTO_DEFINE_FCN0 (GBytes *, gs_local_bytes_unref, g_bytes_unref)
+
+/**
+ * gs_strfreev:
+ *
+ * Call g_strfreev() on a variable location when it goes out of scope.
+ */
+#define gs_strfreev nm_auto(gs_local_strfreev)
+NM_AUTO_DEFINE_FCN (char **, gs_local_strfreev, g_strfreev)
+
+/**
+ * gs_free_error:
+ *
+ * Call g_error_free() on a variable location when it goes out of scope.
+ */
+#define gs_free_error nm_auto(gs_local_free_error)
+NM_AUTO_DEFINE_FCN0 (GError *, gs_local_free_error, g_error_free)
+
+/**
+ * gs_unref_keyfile:
+ *
+ * Call g_key_file_unref() on a variable location when it goes out of scope.
+ */
+#define gs_unref_keyfile nm_auto(gs_local_keyfile_unref)
+NM_AUTO_DEFINE_FCN0 (GKeyFile *, gs_local_keyfile_unref, g_key_file_unref)
 
 /*****************************************************************************/
 
-#define _nm_packed __attribute__ ((packed))
-#define _nm_unused __attribute__ ((unused))
-#define _nm_pure   __attribute__ ((pure))
-#define _nm_const  __attribute__ ((const))
-#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#include "nm-glib.h"
+
+/*****************************************************************************/
 
 #define nm_offsetofend(t,m) (G_STRUCT_OFFSET (t,m) + sizeof (((t *) NULL)->m))
 
-#define nm_auto(fcn) __attribute__ ((cleanup(fcn)))
+/*****************************************************************************/
+
+static inline int nm_close (int fd);
 
 /**
  * nm_auto_free:
  *
  * Call free() on a variable location when it goes out of scope.
+ * This is for pointers that are allocated with malloc() instead of
+ * g_malloc().
+ *
+ * In practice, since glib 2.45, g_malloc()/g_free() always wraps malloc()/free().
+ * See bgo#751592. In that case, it would be safe to free pointers allocated with
+ * malloc() with gs_free or g_free().
+ *
+ * However, let's never mix them. To free malloc'ed memory, always use
+ * free() or nm_auto_free.
  */
+NM_AUTO_DEFINE_FCN_VOID (void *, _nm_auto_free_impl, free)
 #define nm_auto_free nm_auto(_nm_auto_free_impl)
-GS_DEFINE_CLEANUP_FUNCTION(void*, _nm_auto_free_impl, free)
 
-static inline void
-_nm_auto_unset_gvalue_impl (GValue *v)
-{
-       g_value_unset (v);
-}
-#define nm_auto_unset_gvalue nm_auto(_nm_auto_unset_gvalue_impl)
+NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free)
+#define nm_auto_free_variant_iter nm_auto(_nm_auto_free_variant_iter)
+
+NM_AUTO_DEFINE_FCN0 (GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref)
+#define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder)
+
+NM_AUTO_DEFINE_FCN (GList *, _nm_auto_free_list, g_list_free)
+#define nm_auto_free_list nm_auto(_nm_auto_free_list)
+
+NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free)
+#define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free)
+
+#define nm_auto_unset_gvalue nm_auto(g_value_unset)
+
+NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref)
+#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass)
+
+NM_AUTO_DEFINE_FCN0 (GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref)
+#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray)
 
 static inline void
-_nm_auto_free_gstring_impl (GString **str)
+_nm_auto_free_gstring (GString **str)
 {
        if (*str)
                g_string_free (*str, TRUE);
 }
-#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring_impl)
+#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring)
 
 static inline void
-_nm_auto_close_impl (int *pfd)
+_nm_auto_close (int *pfd)
 {
        if (*pfd >= 0) {
                int errsv = errno;
 
-               (void) close (*pfd);
+               (void) nm_close (*pfd);
                errno = errsv;
        }
 }
-#define nm_auto_close nm_auto(_nm_auto_close_impl)
+#define nm_auto_close nm_auto(_nm_auto_close)
 
 static inline void
-_nm_auto_fclose_impl (FILE **pfd)
+_nm_auto_fclose (FILE **pfd)
 {
        if (*pfd) {
                int errsv = errno;
@@ -85,7 +275,7 @@ _nm_auto_fclose_impl (FILE **pfd)
                errno = errsv;
        }
 }
-#define nm_auto_fclose nm_auto(_nm_auto_fclose_impl)
+#define nm_auto_fclose nm_auto(_nm_auto_fclose)
 
 static inline void
 _nm_auto_protect_errno (int *p_saved_errno)
@@ -94,6 +284,26 @@ _nm_auto_protect_errno (int *p_saved_errno)
 }
 #define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_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)
+
+static inline void
+_nm_auto_freev (gpointer ptr)
+{
+       gpointer **p = ptr;
+       gpointer *_ptr;
+
+       if (*p) {
+               for (_ptr = *p; *_ptr; _ptr++)
+                       g_free (*_ptr);
+               g_free (*p);
+       }
+}
+/* g_free a NULL terminated array of pointers, with also freeing each
+ * pointer with g_free(). It essentially does the same as
+ * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */
+#define nm_auto_freev nm_auto(_nm_auto_freev)
+
 /*****************************************************************************/
 
 /* http://stackoverflow.com/a/11172679 */
@@ -106,22 +316,27 @@ _nm_auto_protect_errno (int *p_saved_errno)
 #define __NM_UTILS_MACRO_REST_HELPER_ONE(first)
 #define __NM_UTILS_MACRO_REST_HELPER_TWOORMORE(first, ...)    , __VA_ARGS__
 #define __NM_UTILS_MACRO_REST_NUM(...) \
-    __NM_UTILS_MACRO_REST_SELECT_20TH(__VA_ARGS__, \
+    __NM_UTILS_MACRO_REST_SELECT_30TH(__VA_ARGS__, \
+                TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
+                TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
                 TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
                 TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
                 TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
                 TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway)
-#define __NM_UTILS_MACRO_REST_SELECT_20TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, 
a16, a17, a18, a19, a20, ...) a20
+#define __NM_UTILS_MACRO_REST_SELECT_30TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, 
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, ...) a30
 
 /*****************************************************************************/
 
-/* http://stackoverflow.com/a/2124385/354393 */
+/* http://stackoverflow.com/a/2124385/354393
+ * https://stackoverflow.com/questions/11317474/macro-to-count-number-of-arguments
+ */
 
 #define NM_NARG(...) \
-         _NM_NARG(__VA_ARGS__,_NM_NARG_RSEQ_N())
+         _NM_NARG(, ##__VA_ARGS__, _NM_NARG_RSEQ_N())
 #define _NM_NARG(...) \
          _NM_NARG_ARG_N(__VA_ARGS__)
 #define _NM_NARG_ARG_N( \
+          _0, \
           _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
          _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
          _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
@@ -159,6 +374,7 @@ _nm_auto_protect_errno (int *p_saved_errno)
 #elif defined (__clang__)
 #define NM_PRAGMA_WARNING_DISABLE(warning) \
         _Pragma("clang diagnostic push") \
+        _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \
         _Pragma(_NM_PRAGMA_WARNING_DO(warning))
 #else
 #define NM_PRAGMA_WARNING_DISABLE(warning)
@@ -178,7 +394,7 @@ _nm_auto_protect_errno (int *p_saved_errno)
 
 /**
  * NM_G_ERROR_MSG:
- * @error: (allow none): the #GError instance
+ * @error: (allow-none): the #GError instance
  *
  * All functions must follow the convention that when they
  * return a failure, they must also set the GError to a valid
@@ -191,7 +407,7 @@ _nm_auto_protect_errno (int *p_saved_errno)
 static inline const char *
 NM_G_ERROR_MSG (GError *error)
 {
-       return error ? (error->message ? : "(null)") : "(no-error)"; \
+       return error ? (error->message ?: "(null)") : "(no-error)"; \
 }
 
 /*****************************************************************************/
@@ -199,6 +415,24 @@ NM_G_ERROR_MSG (GError *error)
 /* macro to return strlen() of a compile time string. */
 #define NM_STRLEN(str)     ( sizeof ("" str) - 1 )
 
+/* returns the length of a NULL terminated array of pointers,
+ * like g_strv_length() does. The difference is:
+ *  - it operats on arrays of pointers (of any kind, requiring no cast).
+ *  - it accepts NULL to return zero. */
+#define NM_PTRARRAY_LEN(array) \
+       ({ \
+               typeof (*(array)) *const _array = (array); \
+               gsize _n = 0; \
+               \
+               if (_array) { \
+                       _nm_unused gconstpointer _type_check_is_pointer = _array[0]; \
+                       \
+                       while (_array[_n]) \
+                               _n++; \
+               } \
+               _n; \
+       })
+
 /* Note: @value is only evaluated when *out_val is present.
  * Thus,
  *    NM_SET_OUT (out_str, g_strdup ("hallo"));
@@ -215,6 +449,198 @@ NM_G_ERROR_MSG (GError *error)
 
 /*****************************************************************************/
 
+#ifndef _NM_CC_SUPPORT_AUTO_TYPE
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 )))
+#define _NM_CC_SUPPORT_AUTO_TYPE 1
+#else
+#define _NM_CC_SUPPORT_AUTO_TYPE 0
+#endif
+#endif
+
+#ifndef _NM_CC_SUPPORT_GENERIC
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined 
(__clang__))
+#define _NM_CC_SUPPORT_GENERIC 1
+#else
+#define _NM_CC_SUPPORT_GENERIC 0
+#endif
+#endif
+
+#if _NM_CC_SUPPORT_AUTO_TYPE
+#define _nm_auto_type __auto_type
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \
+       (_Generic ((obj_expr), \
+                  const void        *const: ((const type *) (obj)), \
+                  const void        *     : ((const type *) (obj)), \
+                        void        *const: ((      type *) (obj)), \
+                        void        *     : ((      type *) (obj)), \
+                  const type        *const: ((const type *) (obj)), \
+                  const type        *     : ((const type *) (obj)), \
+                        type        *const: ((      type *) (obj)), \
+                        type        *     : ((      type *) (obj))))
+#define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \
+       (_Generic ((obj_expr), \
+                  const void        *const: ((const type *) (obj)), \
+                  const void        *     : ((const type *) (obj)), \
+                        void        *const: ((      type *) (obj)), \
+                        void        *     : ((      type *) (obj)), \
+                  const alias_type2 *const: ((const type *) (obj)), \
+                  const alias_type2 *     : ((const type *) (obj)), \
+                        alias_type2 *const: ((      type *) (obj)), \
+                        alias_type2 *     : ((      type *) (obj)), \
+                  const type        *const: ((const type *) (obj)), \
+                  const type        *     : ((const type *) (obj)), \
+                        type        *const: ((      type *) (obj)), \
+                        type        *     : ((      type *) (obj))))
+#define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \
+       (_Generic ((obj_expr), \
+                  const void        *const: ((const type *) (obj)), \
+                  const void        *     : ((const type *) (obj)), \
+                        void        *const: ((      type *) (obj)), \
+                        void        *     : ((      type *) (obj)), \
+                  const alias_type2 *const: ((const type *) (obj)), \
+                  const alias_type2 *     : ((const type *) (obj)), \
+                        alias_type2 *const: ((      type *) (obj)), \
+                        alias_type2 *     : ((      type *) (obj)), \
+                  const alias_type3 *const: ((const type *) (obj)), \
+                  const alias_type3 *     : ((const type *) (obj)), \
+                        alias_type3 *const: ((      type *) (obj)), \
+                        alias_type3 *     : ((      type *) (obj)), \
+                  const type        *const: ((const type *) (obj)), \
+                  const type        *     : ((const type *) (obj)), \
+                        type        *const: ((      type *) (obj)), \
+                        type        *     : ((      type *) (obj))))
+#define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \
+       (_Generic ((obj_expr), \
+                  const void        *const: ((const type *) (obj)), \
+                  const void        *     : ((const type *) (obj)), \
+                        void        *const: ((      type *) (obj)), \
+                        void        *     : ((      type *) (obj)), \
+                  const alias_type2 *const: ((const type *) (obj)), \
+                  const alias_type2 *     : ((const type *) (obj)), \
+                        alias_type2 *const: ((      type *) (obj)), \
+                        alias_type2 *     : ((      type *) (obj)), \
+                  const alias_type3 *const: ((const type *) (obj)), \
+                  const alias_type3 *     : ((const type *) (obj)), \
+                        alias_type3 *const: ((      type *) (obj)), \
+                        alias_type3 *     : ((      type *) (obj)), \
+                  const alias_type4 *const: ((const type *) (obj)), \
+                  const alias_type4 *     : ((const type *) (obj)), \
+                        alias_type4 *const: ((      type *) (obj)), \
+                        alias_type4 *     : ((      type *) (obj)), \
+                  const type        *const: ((const type *) (obj)), \
+                  const type        *     : ((const type *) (obj)), \
+                        type        *const: ((      type *) (obj)), \
+                        type        *     : ((      type *) (obj))))
+#define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...)   (_NM_CONSTCAST_FULL_##n (type, obj_expr, obj,    
                    ##__VA_ARGS__))
+#define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...)   (_NM_CONSTCAST_FULL_x   (type, obj_expr, obj, n, 
                    ##__VA_ARGS__))
+#define NM_CONSTCAST_FULL(   type, obj_expr, obj,    ...)   (_NM_CONSTCAST_FULL_y   (type, obj_expr, obj, 
NM_NARG (dummy, ##__VA_ARGS__), ##__VA_ARGS__))
+#else
+#define NM_CONSTCAST_FULL(   type, obj_expr, obj,    ...)   ((type *) (obj))
+#endif
+
+#define NM_CONSTCAST(type, obj, ...) \
+       NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__)
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_UNCONST_PTR(type, arg) \
+       _Generic ((arg), \
+                 const type *: ((type *) (arg)), \
+                       type *: ((type *) (arg)))
+#else
+#define NM_UNCONST_PTR(type, arg) \
+       ((type *) (arg))
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_UNCONST_PPTR(type, arg) \
+       _Generic ((arg), \
+                 const type *     *: ((type **) (arg)), \
+                       type *     *: ((type **) (arg)), \
+                 const type *const*: ((type **) (arg)), \
+                       type *const*: ((type **) (arg)))
+#else
+#define NM_UNCONST_PPTR(type, arg) \
+       ((type **) (arg))
+#endif
+
+#define NM_GOBJECT_CAST(type, obj, is_check, ...) \
+       ({ \
+               const void *_obj = (obj); \
+               \
+               nm_assert (_obj || (is_check (_obj))); \
+               NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \
+       })
+
+#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \
+       ({ \
+               const void *_obj = (obj); \
+               \
+               nm_assert (is_check (_obj)); \
+               NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \
+       })
+
+#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. */
+#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value)))
+#else
+#define _NM_ENSURE_TYPE(type, value) (value)
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+/* these macros cast (value) to
+ *  - "const char **"      (for "MC", mutable-const)
+ *  - "const char *const*" (for "CC", const-const)
+ * The point is to do this cast, but only accepting pointers
+ * that are compatible already.
+ *
+ * The problem is, if you add a function like g_strdupv(), the input
+ * argument is not modified (CC), but you want to make it work also
+ * for "char **". C doesn't allow this form of casting (for good reasons),
+ * so the function makes a choice like g_strdupv(char**). That means,
+ * every time you want to call ith with a const argument, you need to
+ * explicitly cast it.
+ *
+ * These macros do the cast, but they only accept a compatible input
+ * type, otherwise they will fail compilation.
+ */
+#define NM_CAST_STRV_MC(value) \
+       (_Generic ((value), \
+                  const char *     *: (const char *     *) (value), \
+                        char *     *: (const char *     *) (value), \
+                              void *: (const char *     *) (value)))
+#define NM_CAST_STRV_CC(value) \
+       (_Generic ((value), \
+                  const char *const*: (const char *const*) (value), \
+                  const char *     *: (const char *const*) (value), \
+                        char *const*: (const char *const*) (value), \
+                        char *     *: (const char *const*) (value), \
+                        const void *: (const char *const*) (value), \
+                              void *: (const char *const*) (value)))
+#else
+#define NM_CAST_STRV_MC(value) ((const char *     *) (value))
+#define NM_CAST_STRV_CC(value) ((const char *const*) (value))
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_PROPAGATE_CONST(test_expr, ptr) \
+       (_Generic ((test_expr), \
+                  const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \
+                                        default: (_Generic ((test_expr), \
+                                                            typeof (*(test_expr)) *: (ptr)))))
+#else
+#define NM_PROPAGATE_CONST(test_expr, ptr) (ptr)
+#endif
+
+/*****************************************************************************/
+
 #define _NM_IN_SET_EVAL_1( op, _x, y)           (_x == (y))
 #define _NM_IN_SET_EVAL_2( op, _x, y, ...)      (_x == (y)) op _NM_IN_SET_EVAL_1  (op, _x, __VA_ARGS__)
 #define _NM_IN_SET_EVAL_3( op, _x, y, ...)      (_x == (y)) op _NM_IN_SET_EVAL_2  (op, _x, __VA_ARGS__)
@@ -233,21 +659,34 @@ NM_G_ERROR_MSG (GError *error)
 #define _NM_IN_SET_EVAL_16(op, _x, y, ...)      (_x == (y)) op _NM_IN_SET_EVAL_15 (op, _x, __VA_ARGS__)
 
 #define _NM_IN_SET_EVAL_N2(op, _x, n, ...)      (_NM_IN_SET_EVAL_##n(op, _x, __VA_ARGS__))
-#define _NM_IN_SET_EVAL_N(op, x, n, ...)                            \
+#define _NM_IN_SET_EVAL_N(op, type, x, n, ...)                      \
     ({                                                              \
-        typeof(x) _x = (x);                                         \
+        type _x = (x);                                              \
+                                                                    \
+        /* trigger a -Wenum-compare warning */                      \
+        nm_assert (TRUE || _x == (x));                              \
+                                                                    \
         !!_NM_IN_SET_EVAL_N2(op, _x, n, __VA_ARGS__);               \
     })
 
+#define _NM_IN_SET(op, type, x, ...)        _NM_IN_SET_EVAL_N(op, type, x, NM_NARG (__VA_ARGS__), 
__VA_ARGS__)
+
 /* Beware that this does short-circuit evaluation (use "||" instead of "|")
  * which has a possibly unexpected non-function-like behavior.
  * Use NM_IN_SET_SE if you need all arguments to be evaluted. */
-#define NM_IN_SET(x, ...)               _NM_IN_SET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+#define NM_IN_SET(x, ...)                   _NM_IN_SET(||, typeof (x), x, __VA_ARGS__)
 
 /* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do
  * short-circuit evaluation, which can make a difference if the arguments have
  * side-effects. */
-#define NM_IN_SET_SE(x, ...)            _NM_IN_SET_EVAL_N(|, x, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+#define NM_IN_SET_SE(x, ...)                _NM_IN_SET(|,  typeof (x), x, __VA_ARGS__)
+
+/* the *_TYPED forms allow to explicitly select the type of "x". This is useful
+ * if "x" doesn't support typeof (bitfields) or you want to gracefully convert
+ * a type using automatic type conversion rules (but not forcing the conversion
+ * with a cast). */
+#define NM_IN_SET_TYPED(type, x, ...)       _NM_IN_SET(||, type,       x, __VA_ARGS__)
+#define NM_IN_SET_SE_TYPED(type, x, ...)    _NM_IN_SET(|,  type,       x, __VA_ARGS__)
 
 /*****************************************************************************/
 
@@ -278,8 +717,8 @@ _NM_IN_STRSET_streq (const char *x, const char *s)
 #define _NM_IN_STRSET_EVAL_N(op, x, n, ...)                       \
     ({                                                            \
         const char *_x = (x);                                     \
-        (   ((_x == NULL) && _NM_IN_SET_EVAL_N2    (op, (const char *) NULL, n, __VA_ARGS__)) \
-         || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x,                  n, __VA_ARGS__)) \
+        (   ((_x == NULL) && _NM_IN_SET_EVAL_N2    (op, ((const char *) NULL), n, __VA_ARGS__)) \
+         || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x,                    n, __VA_ARGS__)) \
         ); \
     })
 
@@ -357,7 +796,7 @@ _NM_IN_STRSET_streq (const char *x, const char *s)
 
 /* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK
  * with two differences:
- * - @string must be a quited string-literal
+ * - @string must be a quoted string-literal
  * - @fcn must be the full function name, while G_DEFINE_QUARK() appends
  *   "_quark" to the function name.
  * Both properties of G_DEFINE_QUARK() are non favorable, because you can no
@@ -380,6 +819,16 @@ fcn (void) \
 
 /*****************************************************************************/
 
+static inline GString *
+nm_gstring_prepare (GString **l)
+{
+       if (*l)
+               g_string_set_size (*l, 0);
+       else
+               *l = g_string_sized_new (30);
+       return *l;
+}
+
 static inline const char *
 nm_str_not_empty (const char *str)
 {
@@ -451,7 +900,7 @@ nm_str_realloc (char *str)
 
 #define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \
 typedef enum { \
-       _PROPERTY_ENUMS_0, \
+       PROP_0, \
        __VA_ARGS__ \
        _PROPERTY_ENUMS_LAST, \
 } _PropertyEnums; \
@@ -460,30 +909,53 @@ static GParamSpec *obj_properties[_PROPERTY_ENUMS_LAST] = { NULL, }
 #define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \
 static inline void \
-_notify (obj_type *obj, _PropertyEnums prop) \
+_nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums *props) \
 { \
+       const gboolean freeze_thaw = (n > 1); \
+       \
        nm_assert (G_IS_OBJECT (obj)); \
-       nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \
-       g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \
-}
+       nm_assert (n > 0); \
+       \
+       if (freeze_thaw) \
+               g_object_freeze_notify ((GObject *) obj); \
+       while (n-- > 0) { \
+               const _PropertyEnums prop = *props++; \
+               \
+               if (prop != PROP_0) { \
+                       nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \
+                       nm_assert (obj_properties[prop]); \
+                       g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \
+               } \
+       } \
+       if (freeze_thaw) \
+               g_object_thaw_notify ((GObject *) obj); \
+} \
+\
+static inline void \
+_notify (obj_type *obj, _PropertyEnums prop) \
+{ \
+       _nm_gobject_notify_together_impl (obj, 1, &prop); \
+} \
+
+/* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if
+ * there are more than one prop arguments, this will involve a freeze/thaw
+ * of GObject property notifications. */
+#define nm_gobject_notify_together(obj, ...) \
+       _nm_gobject_notify_together_impl (obj, NM_NARG (__VA_ARGS__), (const _PropertyEnums[]) { __VA_ARGS__ 
})
 
 /*****************************************************************************/
 
-#define __NM_GET_PRIVATE(self, type, is_check, result_cmd) \
+#define _NM_GET_PRIVATE(self, type, is_check, ...) (&(NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, 
##__VA_ARGS__)->_priv))
+#if _NM_CC_SUPPORT_AUTO_TYPE
+#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \
        ({ \
-               /* preserve the const-ness of self. Unfortunately, that
-                * way, @self cannot be a void pointer */ \
-               typeof (self) _self = (self); \
+               _nm_auto_type _self = NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__); \
                \
-               /* Get compiler error if variable is of wrong type */ \
-               _nm_unused const type *_self2 = (_self); \
-               \
-               nm_assert (is_check (_self)); \
-               ( result_cmd ); \
+               NM_PROPAGATE_CONST (_self, _self->_priv); \
        })
-
-#define _NM_GET_PRIVATE(self, type, is_check)     __NM_GET_PRIVATE(self, type, is_check, &_self->_priv)
-#define _NM_GET_PRIVATE_PTR(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check,  _self->_priv)
+#else
+#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) (NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, 
##__VA_ARGS__)->_priv)
+#endif
 
 /*****************************************************************************/
 
@@ -495,6 +967,7 @@ nm_g_object_ref (gpointer obj)
                g_object_ref (obj);
        return obj;
 }
+#define nm_g_object_ref(obj) ((typeof (obj)) nm_g_object_ref (obj))
 
 static inline void
 nm_g_object_unref (gpointer obj)
@@ -507,6 +980,58 @@ nm_g_object_unref (gpointer obj)
                g_object_unref (obj);
 }
 
+/* Assigns GObject @obj to destination @pp, and takes an additional ref.
+ * The previous value of @pp is unrefed.
+ *
+ * It makes sure to first increase the ref-count of @obj, and handles %NULL
+ * @obj correctly.
+ * */
+#define nm_g_object_ref_set(pp, obj) \
+       ({ \
+               typeof (*(pp)) *const _pp = (pp); \
+               typeof (*_pp) const _obj = (obj); \
+               typeof (*_pp) _p; \
+               gboolean _changed = FALSE; \
+               \
+               nm_assert (!_pp || !*_pp || G_IS_OBJECT (*_pp)); \
+               nm_assert (!_obj || G_IS_OBJECT (_obj)); \
+               \
+               if (   _pp \
+                   && ((_p = *_pp) != _obj)) { \
+                       nm_g_object_ref (_obj); \
+                       *_pp = _obj; \
+                       nm_g_object_unref (_p); \
+                       _changed = TRUE; \
+               } \
+               _changed; \
+       })
+
+#define nm_clear_pointer(pp, destroy) \
+       ({ \
+               typeof (*(pp)) *_pp = (pp); \
+               typeof (*_pp) _p; \
+               gboolean _changed = FALSE; \
+               \
+               if (   _pp \
+                   && (_p = *_pp)) { \
+                       _nm_unused gconstpointer _p_check_is_pointer = _p; \
+                       \
+                       *_pp = NULL; \
+                       /* g_clear_pointer() assigns @destroy first to a local variable, so that
+                        * you can call "g_clear_pointer (pp, (GDestroyNotify) destroy);" without
+                        * gcc emitting a warning. We don't do that, hence, you cannot cast
+                        * "destroy" first.
+                        *
+                        * On the upside: you are not supposed to cast fcn, because the pointer
+                        * types are preserved. If you really need a cast, you should cast @pp.
+                        * But that is hardly ever necessary. */ \
+                       (destroy) (_p); \
+                       \
+                       _changed = TRUE; \
+               } \
+               _changed; \
+       })
+
 /* basically, replaces
  *   g_clear_pointer (&location, g_free)
  * with
@@ -517,23 +1042,20 @@ nm_g_object_unref (gpointer obj)
  * pointer or points to a const-pointer.
  */
 #define nm_clear_g_free(pp) \
-       ({  \
-               typeof (*(pp)) *_pp = (pp); \
-               typeof (**_pp) *_p = *_pp; \
-               \
-               if (_p) { \
-                       *_pp = NULL; \
-                       g_free (_p); \
-               } \
-               !!_p; \
-       })
+       nm_clear_pointer (pp, g_free)
+
+#define nm_clear_g_object(pp) \
+       nm_clear_pointer (pp, g_object_unref)
 
 static inline gboolean
 nm_clear_g_source (guint *id)
 {
-       if (id && *id) {
-               g_source_remove (*id);
+       guint v;
+
+       if (   id
+           && (v = *id)) {
                *id = 0;
+               g_source_remove (v);
                return TRUE;
        }
        return FALSE;
@@ -542,9 +1064,12 @@ nm_clear_g_source (guint *id)
 static inline gboolean
 nm_clear_g_signal_handler (gpointer self, gulong *id)
 {
-       if (id && *id) {
-               g_signal_handler_disconnect (self, *id);
+       gulong v;
+
+       if (   id
+           && (v = *id)) {
                *id = 0;
+               g_signal_handler_disconnect (self, v);
                return TRUE;
        }
        return FALSE;
@@ -553,9 +1078,12 @@ nm_clear_g_signal_handler (gpointer self, gulong *id)
 static inline gboolean
 nm_clear_g_variant (GVariant **variant)
 {
-       if (variant && *variant) {
-               g_variant_unref (*variant);
+       GVariant *v;
+
+       if (   variant
+           && (v = *variant)) {
                *variant = NULL;
+               g_variant_unref (v);
                return TRUE;
        }
        return FALSE;
@@ -564,10 +1092,13 @@ nm_clear_g_variant (GVariant **variant)
 static inline gboolean
 nm_clear_g_cancellable (GCancellable **cancellable)
 {
-       if (cancellable && *cancellable) {
-               g_cancellable_cancel (*cancellable);
-               g_object_unref (*cancellable);
+       GCancellable *v;
+
+       if (   cancellable
+           && (v = *cancellable)) {
                *cancellable = NULL;
+               g_cancellable_cancel (v);
+               g_object_unref (v);
                return TRUE;
        }
        return FALSE;
@@ -576,18 +1107,65 @@ nm_clear_g_cancellable (GCancellable **cancellable)
 /*****************************************************************************/
 
 /* Determine whether @x is a power of two (@x being an integer type).
- * For the special cases @x equals zero or one, it also returns true.
- * For negative @x, always returns FALSE. That only applies, if the data
- * type of @x is signed. */
+ * Basically, this returns TRUE, if @x has exactly one bit set.
+ * For negative values and zero, this always returns FALSE. */
 #define nm_utils_is_power_of_two(x) ({ \
                typeof(x) __x = (x); \
                \
-               /* Check if the value is negative. In that case, return FALSE.
-                * The first expression is a compile time constant, depending on whether
-                * the type is signed. The second expression is a clumsy way for (__x >= 0),
-                * which causes a compiler warning for unsigned types. */ \
-                   ( ( ((typeof(__x)) -1) > ((typeof(__x)) 0) ) || (__x > 0) || (__x == 0) ) \
-                && ((__x & (__x - 1)) == 0); \
+               (    (__x > ((typeof(__x)) 0)) \
+                && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \
+       })
+
+#define NM_DIV_ROUND_UP(x, y) \
+       ({ \
+               const typeof(x) _x = (x); \
+               const typeof(y) _y = (y); \
+               \
+               (_x / _y + !!(_x % _y)); \
+       })
+
+/*****************************************************************************/
+
+#define NM_UTILS_LOOKUP_DEFAULT(v)            return (v)
+#define NM_UTILS_LOOKUP_DEFAULT_WARN(v)       g_return_val_if_reached (v)
+#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v)  { nm_assert_not_reached (); return (v); }
+#define NM_UTILS_LOOKUP_ITEM(v, n)            (void) 0; case v: return (n); (void) 0
+#define NM_UTILS_LOOKUP_STR_ITEM(v, n)        NM_UTILS_LOOKUP_ITEM(v, ""n"")
+#define NM_UTILS_LOOKUP_ITEM_IGNORE(v)        (void) 0; case v: break; (void) 0
+#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER()   (void) 0; default: break; (void) 0
+
+#define _NM_UTILS_LOOKUP_DEFINE(scope, fcn_name, lookup_type, result_type, unknown_val, ...) \
+scope result_type \
+fcn_name (lookup_type val) \
+{ \
+       switch (val) { \
+               (void) 0, \
+               __VA_ARGS__ \
+               (void) 0; \
+       }; \
+       { unknown_val; } \
+}
+
+#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \
+       _NM_UTILS_LOOKUP_DEFINE (, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__)
+#define NM_UTILS_LOOKUP_STR_DEFINE_STATIC(fcn_name, lookup_type, unknown_val, ...) \
+       _NM_UTILS_LOOKUP_DEFINE (static, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__)
+
+/* Call the string-lookup-table function @fcn_name. If the function returns
+ * %NULL, the numeric index is converted to string using a alloca() buffer.
+ * Beware: this macro uses alloca(). */
+#define NM_UTILS_LOOKUP_STR(fcn_name, idx) \
+       ({ \
+               typeof (idx) _idx = (idx); \
+               const char *_s; \
+               \
+               _s = fcn_name (_idx); \
+               if (!_s) { \
+                       _s = g_alloca (30); \
+                       \
+                       g_snprintf ((char *) _s, 30, "(%lld)", (long long) _idx); \
+               } \
+               _s; \
        })
 
 /*****************************************************************************/
@@ -595,7 +1173,7 @@ nm_clear_g_cancellable (GCancellable **cancellable)
 /* check if @flags has exactly one flag (@check) set. You should call this
  * only with @check being a compile time constant and a power of two. */
 #define NM_FLAGS_HAS(flags, check)  \
-    ( (G_STATIC_ASSERT_EXPR ( ((check) != 0) && ((check) & ((check)-1)) == 0 )), (NM_FLAGS_ANY ((flags), 
(check))) )
+    ( G_STATIC_ASSERT_EXPR ((check) > 0 && ((check) & ((check) - 1)) == 0), NM_FLAGS_ANY ((flags), (check)) )
 
 #define NM_FLAGS_ANY(flags, check)  ( ( ((flags) & (check)) != 0       ) ? TRUE : FALSE )
 #define NM_FLAGS_ALL(flags, check)  ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE )
@@ -658,6 +1236,33 @@ nm_strstrip (char *str)
        return str ? g_strstrip (str) : NULL;
 }
 
+static inline const char *
+nm_strstrip_avoid_copy (const char *str, char **str_free)
+{
+       gsize l;
+       char *s;
+
+       nm_assert (str_free && !*str_free);
+
+       if (!str)
+               return NULL;
+
+       str = nm_str_skip_leading_spaces (str);
+       l = strlen (str);
+       if (   l == 0
+           || !g_ascii_isspace (str[l - 1]))
+               return str;
+       while (   l > 0
+              && g_ascii_isspace (str[l - 1]))
+               l--;
+
+       s = g_new (char, l + 1);
+       memcpy (s, str, l);
+       s[l] = '\0';
+       *str_free = s;
+       return s;
+}
+
 /* g_ptr_array_sort()'s compare function takes pointers to the
  * value. Thus, you cannot use strcmp directly. You can use
  * nm_strcmp_p().
@@ -672,35 +1277,6 @@ nm_strcmp_p (gconstpointer a, gconstpointer b)
        return strcmp (s1, s2);
 }
 
-/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
- * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
- * signature. I guess, in glib there are knowledgeable people that ensure
- * that this additional argument doesn't cause problems due to different ABI
- * for every architecture that glib supports.
- * For NetworkManager, we'd rather avoid such stunts.
- **/
-static inline int
-nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
-{
-       const char *s1 = *((const char **) a);
-       const char *s2 = *((const char **) b);
-
-       return strcmp (s1, s2);
-}
-
-static inline int
-nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
-{
-       const guint32 a = *((const guint32 *) p_a);
-       const guint32 b = *((const guint32 *) p_b);
-
-       if (a < b)
-               return -1;
-       if (a > b)
-               return 1;
-       return 0;
-}
-
 /*****************************************************************************/
 
 /* Taken from systemd's UNIQ_T and UNIQ macros. */
@@ -748,13 +1324,15 @@ nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_d
 /*****************************************************************************/
 
 static inline guint
-nm_encode_version (guint major, guint minor, guint micro) {
+nm_encode_version (guint major, guint minor, guint micro)
+{
        /* analog to the preprocessor macro NM_ENCODE_VERSION(). */
        return (major << 16) | (minor << 8) | micro;
 }
 
 static inline void
-nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
+nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
+{
        *major = (version & 0xFFFF0000u) >> 16;
        *minor = (version & 0x0000FF00u) >>  8;
        *micro = (version & 0x000000FFu);
@@ -802,7 +1380,8 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
                        : "(null)"); \
        })
 
-#define nm_sprintf_buf(buf, format, ...) ({ \
+#define nm_sprintf_buf(buf, format, ...) \
+       ({ \
                char * _buf = (buf); \
                int _buf_len; \
                \
@@ -828,6 +1407,28 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
                _buf; \
        })
 
+/* aims to alloca() a buffer and fill it with printf(format, name).
+ * Note that format must not contain any format specifier except
+ * "%s".
+ * If the resulting string would be too large for stack allocation,
+ * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */
+#define nm_construct_name_a(format, name, p_val_to_free) \
+       ({ \
+               const char *const _name = (name); \
+               char **const _p_val_to_free = (p_val_to_free); \
+               const gsize _name_len = strlen (_name); \
+               char *_buf2; \
+               \
+               nm_assert (_p_val_to_free && !*_p_val_to_free); \
+               if (NM_STRLEN (format) + _name_len < 200) \
+                       _buf2 = nm_sprintf_bufa (NM_STRLEN (format) + _name_len, format, _name); \
+               else { \
+                       _buf2 = g_strdup_printf (format, _name); \
+                       *_p_val_to_free = _buf2; \
+               } \
+               (const char *) _buf2; \
+       })
+
 /*****************************************************************************/
 
 /**
@@ -841,7 +1442,7 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
  * Using _Bool has advantages over gboolean:
  *
  * - commonly _Bool is one byte large, instead of gboolean's 4 bytes (because gboolean
- *   is a typedef for gint). Especially when having boolean fields in a struct, we can
+ *   is a typedef for int). Especially when having boolean fields in a struct, we can
  *   thereby easily save some space.
  *
  * - _Bool type guarantees that two "true" expressions compare equal. E.g. the follwing
@@ -875,7 +1476,6 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
 #define false   0
 #endif
 
-
 #ifdef _G_BOOLEAN_EXPR
 /* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR().
  * As glib's implementation uses a local variable _g_boolean_var_,
@@ -901,7 +1501,57 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
 #define _G_BOOLEAN_EXPR(expr) __NM_G_BOOLEAN_EXPR_IMPL (NM_UNIQ, expr)
 #endif
 
-
 /*****************************************************************************/
 
+/**
+ * nm_steal_int:
+ * @p_val: pointer to an int type.
+ *
+ * Returns: *p_val and sets *p_val to zero the same time.
+ *   Accepts %NULL, in which case also numeric 0 will be returned.
+ */
+#define nm_steal_int(p_val) \
+       ({ \
+               typeof (p_val) const _p_val = (p_val); \
+               typeof (*_p_val) _val = 0; \
+               \
+               if (   _p_val \
+                   && (_val = *_p_val)) { \
+                       *_p_val = 0; \
+               } \
+               _val; \
+       })
+
+static inline int
+nm_steal_fd (int *p_fd)
+{
+       int fd;
+
+       if (   p_fd
+           && ((fd = *p_fd) >= 0)) {
+               *p_fd = -1;
+               return fd;
+       }
+       return -1;
+}
+
+/**
+ * nm_close:
+ *
+ * Like close() but throws an assertion if the input fd is
+ * invalid.  Closing an invalid fd is a programming error, so
+ * it's better to catch it early.
+ */
+static inline int
+nm_close (int fd)
+{
+       int r;
+
+       r = close (fd);
+       nm_assert (r != -1 || fd < 0 || errno != EBADF);
+       return r;
+}
+
+#define NM_PID_T_INVAL ((pid_t) -1)
+
 #endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 413526d..022c065 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -24,6 +24,17 @@
 #include "nm-shared-utils.h"
 
 #include <errno.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <fcntl.h>
+
+/*****************************************************************************/
+
+const void *const _NM_PTRARRAY_EMPTY[1] = { NULL };
+
+/*****************************************************************************/
+
+const NMIPAddr nm_ip_addr_zero = { 0 };
 
 /*****************************************************************************/
 
@@ -86,7 +97,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
 {
        char *p = *buf;
        va_list args;
-       gint retval;
+       int retval;
 
        if (*len == 0)
                return;
@@ -95,7 +106,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
        retval = g_vsnprintf (p, *len, format, args);
        va_end (args);
 
-       if (retval >= *len) {
+       if ((gsize) retval >= *len) {
                *buf = &p[*len];
                *len = 0;
        } else {
@@ -104,6 +115,461 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
        }
 }
 
+/**
+ * nm_utils_strbuf_seek_end:
+ * @buf: the input/output buffer
+ * @len: the input/output lenght of the buffer.
+ *
+ * Commonly, one uses nm_utils_strbuf_append*(), to incrementally
+ * append strings to the buffer. However, sometimes we need to use
+ * existing API to write to the buffer.
+ * After doing so, we want to adjust the buffer counter.
+ * Essentially,
+ *
+ *   g_snprintf (buf, len, ...);
+ *   nm_utils_strbuf_seek_end (&buf, &len);
+ *
+ * is almost the same as
+ *
+ *   nm_utils_strbuf_append (&buf, &len, ...);
+ *
+ * The only difference is the behavior when the string got truncated:
+ * nm_utils_strbuf_append() will recognize that and set the remaining
+ * length to zero.
+ *
+ * In general, the behavior is:
+ *
+ *  - if *len is zero, do nothing
+ *  - if the buffer contains a NUL byte within the first *len characters,
+ *    the buffer is pointed to the NUL byte and len is adjusted. In this
+ *    case, the remaining *len is always >= 1.
+ *    In particular, that is also the case if the NUL byte is at the very last
+ *    position ((*buf)[*len -1]). That happens, when the previous operation
+ *    either fit the string exactly into the buffer or the string was truncated
+ *    by g_snprintf(). The difference cannot be determined.
+ *  - if the buffer contains no NUL bytes within the first *len characters,
+ *    write NUL at the last position, set *len to zero, and point *buf past
+ *    the NUL byte. This would happen with
+ *
+ *       strncpy (buf, long_str, len);
+ *       nm_utils_strbuf_seek_end (&buf, &len).
+ *
+ *    where strncpy() does truncate the string and not NUL terminate it.
+ *    nm_utils_strbuf_seek_end() would then NUL terminate it.
+ */
+void
+nm_utils_strbuf_seek_end (char **buf, gsize *len)
+{
+       gsize l;
+       char *end;
+
+       nm_assert (len);
+       nm_assert (buf && *buf);
+
+       if (*len <= 1) {
+               if (   *len == 1
+                   && (*buf)[0])
+                       goto truncate;
+               return;
+       }
+
+       end = memchr (*buf, 0, *len);
+       if (end) {
+               l = end - *buf;
+               nm_assert (l < *len);
+
+               *buf = end;
+               *len -= l;
+               return;
+       }
+
+truncate:
+       /* hm, no NUL character within len bytes.
+        * Just NUL terminate the array and consume them
+        * all. */
+       *buf += *len;
+       (*buf)[-1] = '\0';
+       *len = 0;
+       return;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_gbytes_equals:
+ * @bytes: (allow-none): a #GBytes array to compare. Note that
+ *   %NULL is treated like an #GBytes array of length zero.
+ * @mem_data: the data pointer with @mem_len bytes
+ * @mem_len: the length of the data pointer
+ *
+ * Returns: %TRUE if @bytes contains the same data as @mem_data. As a
+ *   special case, a %NULL @bytes is treated like an empty array.
+ */
+gboolean
+nm_utils_gbytes_equal_mem (GBytes *bytes,
+                           gconstpointer mem_data,
+                           gsize mem_len)
+{
+       gconstpointer p;
+       gsize l;
+
+       if (!bytes) {
+               /* as a special case, let %NULL GBytes compare idential
+                * to an empty array. */
+               return (mem_len == 0);
+       }
+
+       p = g_bytes_get_data (bytes, &l);
+       return    l == mem_len
+              && (   mem_len == 0 /* allow @mem_data to be %NULL */
+                  || memcmp (p, mem_data, mem_len) == 0);
+}
+
+GVariant *
+nm_utils_gbytes_to_variant_ay (GBytes *bytes)
+{
+       const guint8 *p;
+       gsize l;
+
+       if (!bytes) {
+               /* for convenience, accept NULL to return an empty variant */
+               return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0);
+       }
+
+       p = g_bytes_get_data (bytes, &l);
+       return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1);
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_strquote:
+ * @buf: the output buffer of where to write the quoted @str argument.
+ * @buf_len: the size of @buf.
+ * @str: (allow-none): the string to quote.
+ *
+ * Writes @str to @buf with quoting. The resulting buffer
+ * is always NUL terminated, unless @buf_len is zero.
+ * If @str is %NULL, it writes "(null)".
+ *
+ * If @str needs to be truncated, the closing quote is '^' instead
+ * of '"'.
+ *
+ * This is similar to nm_strquote_a(), which however uses alloca()
+ * to allocate a new buffer. Also, here @buf_len is the size of @buf,
+ * while nm_strquote_a() has the number of characters to print. The latter
+ * doesn't include the quoting.
+ *
+ * Returns: the input buffer with the quoted string.
+ */
+const char *
+nm_strquote (char *buf, gsize buf_len, const char *str)
+{
+       const char *const buf0 = buf;
+
+       if (!str) {
+               nm_utils_strbuf_append_str (&buf, &buf_len, "(null)");
+               goto out;
+       }
+
+       if (G_UNLIKELY (buf_len <= 2)) {
+               switch (buf_len) {
+               case 2:
+                       *(buf++) = '^';
+                       /* fall-through */
+               case 1:
+                       *(buf++) = '\0';
+                       break;
+               }
+               goto out;
+       }
+
+       *(buf++) = '"';
+       buf_len--;
+
+       nm_utils_strbuf_append_str (&buf, &buf_len, str);
+
+       /* if the string was too long we indicate truncation with a
+        * '^' instead of a closing quote. */
+       if (G_UNLIKELY (buf_len <= 1)) {
+               switch (buf_len) {
+               case 1:
+                       buf[-1] = '^';
+                       break;
+               case 0:
+                       buf[-2] = '^';
+                       break;
+               default:
+                       nm_assert_not_reached ();
+                       break;
+               }
+       } else {
+               nm_assert (buf_len >= 2);
+               *(buf++) = '"';
+               *(buf++) = '\0';
+       }
+
+out:
+       return buf0;
+}
+
+/*****************************************************************************/
+
+char _nm_utils_to_string_buffer[];
+
+void
+nm_utils_to_string_buffer_init (char **buf, gsize *len)
+{
+       if (!*buf) {
+               *buf = _nm_utils_to_string_buffer;
+               *len = sizeof (_nm_utils_to_string_buffer);
+       }
+}
+
+gboolean
+nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len)
+{
+       nm_utils_to_string_buffer_init (buf, len);
+       if (!obj) {
+               g_strlcpy (*buf, "(null)", *len);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
+                    gsize n_descs,
+                    unsigned flags,
+                    char *buf,
+                    gsize len)
+{
+       gsize i;
+       char *p;
+
+#if NM_MORE_ASSERTS > 10
+       nm_assert (descs);
+       nm_assert (n_descs > 0);
+       for (i = 0; i < n_descs; i++) {
+               gsize j;
+
+               nm_assert (descs[i].name && descs[i].name[0]);
+               for (j = 0; j < i; j++)
+                       nm_assert (descs[j].flag != descs[i].flag);
+       }
+#endif
+
+       nm_utils_to_string_buffer_init (&buf, &len);
+
+       if (!len)
+               return buf;
+
+       buf[0] = '\0';
+       p = buf;
+       if (!flags) {
+               for (i = 0; i < n_descs; i++) {
+                       if (!descs[i].flag) {
+                               nm_utils_strbuf_append_str (&p, &len, descs[i].name);
+                               break;
+                       }
+               }
+               return buf;
+       }
+
+       for (i = 0; flags && i < n_descs; i++) {
+               if (   descs[i].flag
+                   && NM_FLAGS_ALL (flags, descs[i].flag)) {
+                       flags &= ~descs[i].flag;
+
+                       if (buf[0] != '\0')
+                               nm_utils_strbuf_append_c (&p, &len, ',');
+                       nm_utils_strbuf_append_str (&p, &len, descs[i].name);
+               }
+       }
+       if (flags) {
+               if (buf[0] != '\0')
+                       nm_utils_strbuf_append_c (&p, &len, ',');
+               nm_utils_strbuf_append (&p, &len, "0x%x", flags);
+       }
+       return buf;
+};
+
+/*****************************************************************************/
+
+/**
+ * _nm_utils_ip4_prefix_to_netmask:
+ * @prefix: a CIDR prefix
+ *
+ * Returns: the netmask represented by the prefix, in network byte order
+ **/
+guint32
+_nm_utils_ip4_prefix_to_netmask (guint32 prefix)
+{
+       return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
+}
+
+/**
+ * _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)
+{
+       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 */
+
+       return 24;  /* Class C - 255.255.255.0 */
+}
+
+gboolean
+nm_utils_ip_is_site_local (int addr_family,
+                           const void *address)
+{
+       in_addr_t addr4;
+
+       switch (addr_family) {
+       case AF_INET:
+               /* RFC1918 private addresses
+                * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */
+               addr4 = ntohl (*((const in_addr_t *) address));
+               return    (addr4 & 0xff000000) == 0x0a000000
+                      || (addr4 & 0xfff00000) == 0xac100000
+                      || (addr4 & 0xffff0000) == 0xc0a80000;
+       case AF_INET6:
+               return IN6_IS_ADDR_SITELOCAL (address);
+       default:
+               g_return_val_if_reached (FALSE);
+       }
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_utils_parse_inaddr_bin (int addr_family,
+                           const char *text,
+                           gpointer out_addr)
+{
+       NMIPAddr addrbin;
+
+       g_return_val_if_fail (text, FALSE);
+
+       if (addr_family == AF_UNSPEC)
+               addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
+       else
+               g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
+
+       /* use a temporary variable @addrbin, to guarantee that @out_addr
+        * is only modified on success. */
+       if (inet_pton (addr_family, text, &addrbin) != 1)
+               return FALSE;
+
+       if (out_addr) {
+               switch (addr_family) {
+               case AF_INET:
+                       *((in_addr_t *) out_addr) = addrbin.addr4;
+                       break;
+               case AF_INET6:
+                       *((struct in6_addr *) out_addr) = addrbin.addr6;
+                       break;
+               default:
+                       nm_assert_not_reached ();
+               }
+       }
+       return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr (int addr_family,
+                       const char *text,
+                       char **out_addr)
+{
+       NMIPAddr addrbin;
+       char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+
+       nm_assert (!out_addr || !*out_addr);
+
+       if (!nm_utils_parse_inaddr_bin (addr_family, text, &addrbin))
+               return FALSE;
+       NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof 
(addrstr_buf))));
+       return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr_prefix_bin (int addr_family,
+                                  const char *text,
+                                  gpointer out_addr,
+                                  int *out_prefix)
+{
+       gs_free char *addrstr_free = NULL;
+       int prefix = -1;
+       const char *slash;
+       const char *addrstr;
+       NMIPAddr addrbin;
+       int addr_len;
+
+       g_return_val_if_fail (text, FALSE);
+
+       if (addr_family == AF_UNSPEC)
+               addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
+
+       if (addr_family == AF_INET)
+               addr_len = sizeof (in_addr_t);
+       else if (addr_family == AF_INET6)
+               addr_len = sizeof (struct in6_addr);
+       else
+               g_return_val_if_reached (FALSE);
+
+       slash = strchr (text, '/');
+       if (slash)
+               addrstr = addrstr_free = g_strndup (text, slash - text);
+       else
+               addrstr = text;
+
+       if (inet_pton (addr_family, addrstr, &addrbin) != 1)
+               return FALSE;
+
+       if (slash) {
+               prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10,
+                                                      0,
+                                                      addr_family == AF_INET ? 32 : 128,
+                                                      -1);
+               if (prefix == -1)
+                       return FALSE;
+       }
+
+       if (out_addr)
+               memcpy (out_addr, &addrbin, addr_len);
+       NM_SET_OUT (out_prefix, prefix);
+       return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr_prefix (int addr_family,
+                              const char *text,
+                              char **out_addr,
+                              int *out_prefix)
+{
+       NMIPAddr addrbin;
+       char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+
+       if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addrbin, out_prefix))
+               return FALSE;
+       NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof 
(addrstr_buf))));
+       return TRUE;
+}
+
 /*****************************************************************************/
 
 /* _nm_utils_ascii_str_to_int64:
@@ -158,6 +624,286 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma
 
 /*****************************************************************************/
 
+/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
+ * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
+ * signature. I guess, in glib there are knowledgeable people that ensure
+ * that this additional argument doesn't cause problems due to different ABI
+ * for every architecture that glib supports.
+ * For NetworkManager, we'd rather avoid such stunts.
+ **/
+int
+nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       const char *s1 = *((const char **) a);
+       const char *s2 = *((const char **) b);
+
+       return strcmp (s1, s2);
+}
+
+int
+nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+       const guint32 a = *((const guint32 *) p_a);
+       const guint32 b = *((const guint32 *) p_b);
+
+       if (a < b)
+               return -1;
+       if (a > b)
+               return 1;
+       return 0;
+}
+
+int
+nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+       /* p_a and p_b are two pointers to a pointer, where the pointer is
+        * interpreted as a integer using GPOINTER_TO_INT().
+        *
+        * That is the case of a hash-table that uses GINT_TO_POINTER() to
+        * convert integers as pointers, and the resulting keys-as-array
+        * array. */
+       const int a = GPOINTER_TO_INT (*((gconstpointer *) p_a));
+       const int b = GPOINTER_TO_INT (*((gconstpointer *) p_b));
+
+       if (a < b)
+               return -1;
+       if (a > b)
+               return 1;
+       return 0;
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_dbus_path_get_last_component (const char *dbus_path)
+{
+       if (dbus_path) {
+               dbus_path = strrchr (dbus_path, '/');
+               if (dbus_path)
+                       return dbus_path + 1;
+       }
+       return NULL;
+}
+
+static gint64
+_dbus_path_component_as_num (const char *p)
+{
+       gint64 n;
+
+       /* no odd stuff. No leading zeros, only a non-negative, decimal integer.
+        *
+        * Otherwise, there would be multiple ways to encode the same number "10"
+        * and "010". That is just confusing. A number has no leading zeros,
+        * if it has, it's not a number (as far as we are concerned here). */
+       if (p[0] == '0') {
+               if (p[1] != '\0')
+                       return -1;
+               else
+                       return 0;
+       }
+       if (!(p[0] >= '1' && p[0] <= '9'))
+               return -1;
+       if (!NM_STRCHAR_ALL (&p[1], ch, (ch >= '0' && ch <= '9')))
+               return -1;
+       n = _nm_utils_ascii_str_to_int64 (p, 10, 0, G_MAXINT64, -1);
+       nm_assert (n == -1 || nm_streq0 (p, nm_sprintf_bufa (100, "%"G_GINT64_FORMAT, n)));
+       return n;
+}
+
+int
+nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b)
+{
+       const char *l_a, *l_b;
+       gsize plen;
+       gint64 n_a, n_b;
+
+       /* compare function for two D-Bus paths. It behaves like
+        * strcmp(), except, if both paths have the same prefix,
+        * and both end in a (positive) number, then the paths
+        * will be sorted by number. */
+
+       NM_CMP_SELF (dbus_path_a, dbus_path_b);
+
+       /* if one or both paths have no slash (and no last component)
+        * compare the full paths directly. */
+       if (   !(l_a = nm_utils_dbus_path_get_last_component (dbus_path_a))
+           || !(l_b = nm_utils_dbus_path_get_last_component (dbus_path_b)))
+               goto comp_full;
+
+       /* check if both paths have the same prefix (up to the last-component). */
+       plen = l_a - dbus_path_a;
+       if (plen != (l_b - dbus_path_b))
+               goto comp_full;
+       NM_CMP_RETURN (strncmp (dbus_path_a, dbus_path_b, plen));
+
+       n_a = _dbus_path_component_as_num (l_a);
+       n_b = _dbus_path_component_as_num (l_b);
+       if (n_a == -1 && n_b == -1)
+               goto comp_l;
+
+       /* both components must be convertiable to a number. If they are not,
+        * (and only one of them is), then we must always strictly sort numeric parts
+        * after non-numeric components. If we wouldn't, we wouldn't have
+        * a total order.
+        *
+        * An example of a not total ordering would be:
+        *   "8"   < "010"  (numeric)
+        *   "0x"  < "8"    (lexical)
+        *   "0x"  > "010"  (lexical)
+        * We avoid this, by forcing that a non-numeric entry "0x" always sorts
+        * before numeric entries.
+        *
+        * Additionally, _dbus_path_component_as_num() would also reject "010" as
+        * not a valid number.
+        */
+       if (n_a == -1)
+               return -1;
+       if (n_b == -1)
+               return 1;
+
+       NM_CMP_DIRECT (n_a, n_b);
+       nm_assert (nm_streq (dbus_path_a, dbus_path_b));
+       return 0;
+
+comp_full:
+       NM_CMP_DIRECT_STRCMP0 (dbus_path_a, dbus_path_b);
+       return 0;
+comp_l:
+       NM_CMP_DIRECT_STRCMP0 (l_a, l_b);
+       nm_assert (nm_streq (dbus_path_a, dbus_path_b));
+       return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_strsplit_set:
+ * @str: the string to split.
+ * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n",
+ *   like bash's $IFS.
+ * @allow_escaping: whether delimiters can be escaped by a backslash
+ *
+ * This is a replacement for g_strsplit_set() which avoids copying
+ * each word once (the entire strv array), but instead copies it once
+ * and all words point into that internal copy.
+ *
+ * Another difference from g_strsplit_set() is that this never returns
+ * empty words. Multiple delimiters are combined and treated as one.
+ *
+ * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are
+ * not treated as a separator. Such delimiters and their escape
+ * character are copied to the current word without unescaping them.
+ *
+ * Returns: %NULL if @str is %NULL or contains only delimiters.
+ *   Otherwise, a %NULL terminated strv array containing non-empty
+ *   words, split at the delimiter characters (delimiter characters
+ *   are removed).
+ *   The strings to which the result strv array points to are allocated
+ *   after the returned result itself. Don't free the strings themself,
+ *   but free everything with g_free().
+ */
+const char **
+nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping)
+{
+       const char **ptr, **ptr0;
+       gsize alloc_size, plen, i;
+       gsize str_len;
+       char *s0;
+       char *s;
+       guint8 delimiters_table[256];
+       gboolean escaped = FALSE;
+
+       if (!str)
+               return NULL;
+
+       /* initialize lookup table for delimiter */
+       if (!delimiters)
+               delimiters = " \t\n";
+       memset (delimiters_table, 0, sizeof (delimiters_table));
+       for (i = 0; delimiters[i]; i++)
+               delimiters_table[(guint8) delimiters[i]] = 1;
+
+#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \
+       ((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc))
+
+#define next_char(p, esc) \
+       G_STMT_START { \
+               if (esc) \
+                       esc = FALSE; \
+               else \
+                       esc = p[0] == '\\'; \
+               p++; \
+       } G_STMT_END
+
+       /* skip initial delimiters, and return of the remaining string is
+        * empty. */
+       while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped))
+               next_char (str, escaped);
+
+       if (!str[0])
+               return NULL;
+
+       str_len = strlen (str) + 1;
+       alloc_size = 8;
+
+       /* we allocate the buffer larger, so to copy @str at the
+        * end of it as @s0. */
+       ptr0 = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
+       s0 = (char *) &ptr0[alloc_size + 1];
+       memcpy (s0, str, str_len);
+
+       plen = 0;
+       s = s0;
+       ptr = ptr0;
+
+       while (TRUE) {
+               if (plen >= alloc_size) {
+                       const char **ptr_old = ptr;
+
+                       /* reallocate the buffer. Note that for now the string
+                        * continues to be in ptr0/s0. We fix that at the end. */
+                       alloc_size *= 2;
+                       ptr = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
+                       memcpy (ptr, ptr_old, sizeof (const char *) * plen);
+                       if (ptr_old != ptr0)
+                               g_free (ptr_old);
+               }
+
+               ptr[plen++] = s;
+
+               nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped));
+
+               while (TRUE) {
+                       next_char (s, escaped);
+                       if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped))
+                               break;
+                       if (s[0] == '\0')
+                               goto done;
+               }
+
+               s[0] = '\0';
+               next_char (s, escaped);
+               while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped))
+                       next_char (s, escaped);
+               if (s[0] == '\0')
+                       break;
+       }
+done:
+       ptr[plen] = NULL;
+
+       if (ptr != ptr0) {
+               /* we reallocated the buffer. We must copy over the
+                * string @s0 and adjust the pointers. */
+               s = (char *) &ptr[alloc_size + 1];
+               memcpy (s, s0, str_len);
+               for (i = 0; i < plen; i++)
+                       ptr[i] = &s[ptr[i] - s0];
+               g_free (ptr0);
+       }
+
+       return ptr;
+}
+
 /**
  * nm_utils_strv_find_first:
  * @list: the strv list to search
@@ -204,11 +950,40 @@ nm_utils_strv_find_first (char **list, gssize len, const char *needle)
        return -1;
 }
 
+char **
+_nm_utils_strv_cleanup (char **strv,
+                        gboolean strip_whitespace,
+                        gboolean skip_empty,
+                        gboolean skip_repeated)
+{
+       guint i, j;
+
+       if (!strv || !*strv)
+               return strv;
+
+       if (strip_whitespace) {
+               for (i = 0; strv[i]; i++)
+                       g_strstrip (strv[i]);
+       }
+       if (!skip_empty && !skip_repeated)
+               return strv;
+       j = 0;
+       for (i = 0; strv[i]; i++) {
+               if (   (skip_empty && !*strv[i])
+                   || (skip_repeated && nm_utils_strv_find_first (strv, j, strv[i]) >= 0))
+                       g_free (strv[i]);
+               else
+                       strv[j++] = strv[i];
+       }
+       strv[j] = NULL;
+       return strv;
+}
+
 /*****************************************************************************/
 
-gint
+int
 _nm_utils_ascii_str_to_bool (const char *str,
-                             gint default_value)
+                             int default_value)
 {
        gsize len;
        char *s = NULL;
@@ -290,7 +1065,7 @@ nm_utils_error_is_cancelled (GError *error,
  */
 gboolean
 nm_g_object_set_property (GObject *object,
-                          const gchar  *property_name,
+                          const char   *property_name,
                           const GValue *value,
                           GError **error)
 {
@@ -363,4 +1138,934 @@ nm_g_object_set_property (GObject *object,
        return TRUE;
 }
 
-/*****************************************************************************/
+gboolean
+nm_g_object_set_property_boolean (GObject *object,
+                                  const char   *property_name,
+                                  gboolean value,
+                                  GError **error)
+{
+       nm_auto_unset_gvalue GValue gvalue = { 0 };
+
+       g_value_init (&gvalue, G_TYPE_BOOLEAN);
+       g_value_set_boolean (&gvalue, !!value);
+       return nm_g_object_set_property (object, property_name, &gvalue, error);
+}
+
+gboolean
+nm_g_object_set_property_uint (GObject *object,
+                               const char   *property_name,
+                               guint value,
+                               GError **error)
+{
+       nm_auto_unset_gvalue GValue gvalue = { 0 };
+
+       g_value_init (&gvalue, G_TYPE_UINT);
+       g_value_set_uint (&gvalue, value);
+       return nm_g_object_set_property (object, property_name, &gvalue, error);
+}
+
+GParamSpec *
+nm_g_object_class_find_property_from_gtype (GType gtype,
+                                            const char *property_name)
+{
+       nm_auto_unref_gtypeclass GObjectClass *gclass = NULL;
+
+       gclass = g_type_class_ref (gtype);
+       return g_object_class_find_property (gclass, property_name);
+}
+
+/*****************************************************************************/
+
+static void
+_str_append_escape (GString *s, char ch)
+{
+       g_string_append_c (s, '\\');
+       g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07));
+       g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07));
+       g_string_append_c (s, '0' + ( ((guchar) ch)       & 07));
+}
+
+gconstpointer
+nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free)
+{
+       GString *gstr;
+       gsize len;
+       const char *s;
+
+       g_return_val_if_fail (to_free, NULL);
+       g_return_val_if_fail (out_len, NULL);
+
+       if (!str) {
+               *out_len = 0;
+               *to_free = NULL;
+               return NULL;
+       }
+
+       len = strlen (str);
+
+       s = memchr (str, '\\', len);
+       if (!s) {
+               *out_len = len;
+               *to_free = NULL;
+               return str;
+       }
+
+       gstr = g_string_new_len (NULL, len);
+
+       g_string_append_len (gstr, str, s - str);
+       str = s;
+
+       for (;;) {
+               char ch;
+               guint v;
+
+               nm_assert (str[0] == '\\');
+
+               ch = (++str)[0];
+
+               if (ch == '\0') {
+                       // error. Trailing '\\'
+                       break;
+               }
+
+               if (ch >= '0' && ch <= '9') {
+                       v = ch - '0';
+                       ch = (++str)[0];
+                       if (ch >= '0' && ch <= '7') {
+                               v = v * 8 + (ch - '0');
+                               ch = (++str)[0];
+                               if (ch >= '0' && ch <= '7') {
+                                       v = v * 8 + (ch - '0');
+                                       ch = (++str)[0];
+                               }
+                       }
+                       ch = v;
+               } else {
+                       switch (ch) {
+                       case 'b': ch = '\b'; break;
+                       case 'f': ch = '\f'; break;
+                       case 'n': ch = '\n'; break;
+                       case 'r': ch = '\r'; break;
+                       case 't': ch = '\t'; break;
+                       case 'v': ch = '\v'; break;
+                       default:
+                               /* Here we handle "\\\\", but all other unexpected escape sequences are 
really a bug.
+                                * Take them literally, after removing the escape character */
+                               break;
+                       }
+                       str++;
+               }
+
+               g_string_append_c (gstr, ch);
+
+               s = strchr (str, '\\');
+               if (!s) {
+                       g_string_append (gstr, str);
+                       break;
+               }
+
+               g_string_append_len (gstr, str, s - str);
+               str = s;
+       }
+
+       *out_len = gstr->len;
+       *to_free = gstr->str;
+       return g_string_free (gstr, FALSE);
+}
+
+/**
+ * nm_utils_buf_utf8safe_escape:
+ * @buf: byte array, possibly in utf-8 encoding, may have NUL characters.
+ * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated
+ *   string.
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ * @to_free: (out): return the pointer location of the string
+ *   if a copying was necessary.
+ *
+ * Based on the assumption, that @buf contains UTF-8 encoded bytes,
+ * this will return valid UTF-8 sequence, and invalid sequences
+ * will be escaped with backslash (C escaping, like g_strescape()).
+ * This is sanitize non UTF-8 characters. The result is valid
+ * UTF-8.
+ *
+ * The operation can be reverted with nm_utils_buf_utf8safe_unescape().
+ * Note that if, and only if @buf contains no NUL bytes, the operation
+ * can also be reverted with g_strcompress().
+ *
+ * Depending on @flags, valid UTF-8 characters are not escaped at all
+ * (except the escape character '\\'). This is the difference to g_strescape(),
+ * which escapes all non-ASCII characters. This allows to pass on
+ * valid UTF-8 characters as-is and can be directly shown to the user
+ * as UTF-8 -- with exception of the backslash escape character,
+ * invalid UTF-8 sequences, and other (depending on @flags).
+ *
+ * Returns: the escaped input buffer, as valid UTF-8. If no escaping
+ *   is necessary, it returns the input @buf. Otherwise, an allocated
+ *   string @to_free is returned which must be freed by the caller
+ *   with g_free. The escaping can be reverted by g_strcompress().
+ **/
+const char *
+nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char 
**to_free)
+{
+       const char *const str = buf;
+       const char *p = NULL;
+       const char *s;
+       gboolean nul_terminated = FALSE;
+       GString *gstr;
+
+       g_return_val_if_fail (to_free, NULL);
+
+       *to_free = NULL;
+
+       if (buflen == 0)
+               return NULL;
+
+       if (buflen < 0) {
+               if (!str)
+                       return NULL;
+               buflen = strlen (str);
+               if (buflen == 0)
+                       return str;
+               nul_terminated = TRUE;
+       }
+
+       if (   g_utf8_validate (str, buflen, &p)
+           && nul_terminated) {
+               /* note that g_utf8_validate() does not allow NUL character inside @str. Good.
+                * We can treat @str like a NUL terminated string. */
+               if (!NM_STRCHAR_ANY (str, ch,
+                                       (   ch == '\\' \
+                                        || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) 
\
+                                            && ch < ' ') \
+                                        || (   NM_FLAGS_HAS (flags, 
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
+                                            && ((guchar) ch) >= 127))))
+                       return str;
+       }
+
+       gstr = g_string_sized_new (buflen + 5);
+
+       s = str;
+       do {
+               buflen -= p - s;
+               nm_assert (buflen >= 0);
+
+               for (; s < p; s++) {
+                       char ch = s[0];
+
+                       if (ch == '\\')
+                               g_string_append (gstr, "\\\\");
+                       else if (   (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \
+                                    && ch < ' ') \
+                                || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
+                                    && ((guchar) ch) >= 127))
+                               _str_append_escape (gstr, ch);
+                       else
+                               g_string_append_c (gstr, ch);
+               }
+
+               if (buflen <= 0)
+                       break;
+
+               _str_append_escape (gstr, p[0]);
+
+               buflen--;
+               if (buflen == 0)
+                       break;
+
+               s = &p[1];
+               g_utf8_validate (s, buflen, &p);
+       } while (TRUE);
+
+       *to_free = g_string_free (gstr, FALSE);
+       return *to_free;
+}
+
+const char *
+nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free)
+{
+       gconstpointer p;
+       gsize l;
+
+       if (bytes)
+               p = g_bytes_get_data (bytes, &l);
+       else {
+               p = NULL;
+               l = 0;
+       }
+
+       return nm_utils_buf_utf8safe_escape (p, l, flags, to_free);
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_str_utf8safe_unescape (const char *str, char **to_free)
+{
+       g_return_val_if_fail (to_free, NULL);
+
+       if (!str || !strchr (str, '\\')) {
+               *to_free = NULL;
+               return str;
+       }
+       return (*to_free = g_strcompress (str));
+}
+
+/**
+ * nm_utils_str_utf8safe_escape:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ * @to_free: (out): return the pointer location of the string
+ *   if a copying was necessary.
+ *
+ * Returns the possible non-UTF-8 NUL terminated string @str
+ * and uses backslash escaping (C escaping, like g_strescape())
+ * to sanitize non UTF-8 characters. The result is valid
+ * UTF-8.
+ *
+ * The operation can be reverted with g_strcompress() or
+ * nm_utils_str_utf8safe_unescape().
+ *
+ * Depending on @flags, valid UTF-8 characters are not escaped at all
+ * (except the escape character '\\'). This is the difference to g_strescape(),
+ * which escapes all non-ASCII characters. This allows to pass on
+ * valid UTF-8 characters as-is and can be directly shown to the user
+ * as UTF-8 -- with exception of the backslash escape character,
+ * invalid UTF-8 sequences, and other (depending on @flags).
+ *
+ * Returns: the escaped input string, as valid UTF-8. If no escaping
+ *   is necessary, it returns the input @str. Otherwise, an allocated
+ *   string @to_free is returned which must be freed by the caller
+ *   with g_free. The escaping can be reverted by g_strcompress().
+ **/
+const char *
+nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free)
+{
+       return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free);
+}
+
+/**
+ * nm_utils_str_utf8safe_escape_cp:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ *
+ * Like nm_utils_str_utf8safe_escape(), except the returned value
+ * is always a copy of the input and must be freed by the caller.
+ *
+ * Returns: the escaped input string in UTF-8 encoding. The returned
+ *   value should be freed with g_free().
+ *   The escaping can be reverted by g_strcompress().
+ **/
+char *
+nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+       char *s;
+
+       nm_utils_str_utf8safe_escape (str, flags, &s);
+       return s ?: g_strdup (str);
+}
+
+char *
+nm_utils_str_utf8safe_unescape_cp (const char *str)
+{
+       return str ? g_strcompress (str) : NULL;
+}
+
+char *
+nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+       char *str_to_free;
+
+       nm_utils_str_utf8safe_escape (str, flags, &str_to_free);
+       if (str_to_free) {
+               g_free (str);
+               return str_to_free;
+       }
+       return str;
+}
+
+/*****************************************************************************/
+
+/* taken from systemd's fd_wait_for_event(). Note that the timeout
+ * is here in nano-seconds, not micro-seconds. */
+int
+nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns)
+{
+       struct pollfd pollfd = {
+               .fd = fd,
+               .events = event,
+       };
+       struct timespec ts, *pts;
+       int r;
+
+       if (timeout_ns < 0)
+               pts = NULL;
+       else {
+               ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND);
+               ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND);
+               pts = &ts;
+       }
+
+       r = ppoll (&pollfd, 1, pts, NULL);
+       if (r < 0)
+               return -errno;
+       if (r == 0)
+               return 0;
+       return pollfd.revents;
+}
+
+/* taken from systemd's loop_read() */
+ssize_t
+nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+       uint8_t *p = buf;
+       ssize_t n = 0;
+
+       g_return_val_if_fail (fd >= 0, -EINVAL);
+       g_return_val_if_fail (buf, -EINVAL);
+
+       /* If called with nbytes == 0, let's call read() at least
+        * once, to validate the operation */
+
+       if (nbytes > (size_t) SSIZE_MAX)
+               return -EINVAL;
+
+       do {
+               ssize_t k;
+
+               k = read (fd, p, nbytes);
+               if (k < 0) {
+                       if (errno == EINTR)
+                               continue;
+
+                       if (errno == EAGAIN && do_poll) {
+
+                               /* We knowingly ignore any return value here,
+                                * and expect that any error/EOF is reported
+                                * via read() */
+
+                               (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1);
+                               continue;
+                       }
+
+                       return n > 0 ? n : -errno;
+               }
+
+               if (k == 0)
+                       return n;
+
+               g_assert ((size_t) k <= nbytes);
+
+               p += k;
+               nbytes -= k;
+               n += k;
+       } while (nbytes > 0);
+
+       return n;
+}
+
+/* taken from systemd's loop_read_exact() */
+int
+nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+       ssize_t n;
+
+       n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll);
+       if (n < 0)
+               return (int) n;
+       if ((size_t) n != nbytes)
+               return -EIO;
+
+       return 0;
+}
+
+NMUtilsNamedValue *
+nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
+{
+       GHashTableIter iter;
+       NMUtilsNamedValue *values;
+       guint i, len;
+
+       if (   !hash
+           || !(len = g_hash_table_size (hash))) {
+               NM_SET_OUT (out_len, 0);
+               return NULL;
+       }
+
+       i = 0;
+       values = g_new (NMUtilsNamedValue, len + 1);
+       g_hash_table_iter_init (&iter, hash);
+       while (g_hash_table_iter_next (&iter,
+                                      (gpointer *) &values[i].name,
+                                      (gpointer *) &values[i].value_ptr))
+               i++;
+       nm_assert (i == len);
+       values[i].name = NULL;
+       values[i].value_ptr = NULL;
+
+       if (len > 1) {
+               g_qsort_with_data (values, len, sizeof (values[0]),
+                                  nm_utils_named_entry_cmp_with_data, NULL);
+       }
+
+       NM_SET_OUT (out_len, len);
+       return values;
+}
+
+gpointer *
+nm_utils_hash_keys_to_array (GHashTable *hash,
+                             GCompareDataFunc compare_func,
+                             gpointer user_data,
+                             guint *out_len)
+{
+       guint len;
+       gpointer *keys;
+
+       /* by convention, we never return an empty array. In that
+        * case, always %NULL. */
+       if (   !hash
+           || g_hash_table_size (hash) == 0) {
+               NM_SET_OUT (out_len, 0);
+               return NULL;
+       }
+
+       keys = g_hash_table_get_keys_as_array (hash, &len);
+       if (   len > 1
+           && compare_func) {
+               g_qsort_with_data (keys,
+                                  len,
+                                  sizeof (gpointer),
+                                  compare_func,
+                                  user_data);
+       }
+       NM_SET_OUT (out_len, len);
+       return keys;
+}
+
+char **
+nm_utils_strv_make_deep_copied (const char **strv)
+{
+       gsize i;
+
+       /* it takes a strv dictionary, and copies each
+        * strings. Note that this updates @strv *in-place*
+        * and returns it. */
+
+       if (!strv)
+               return NULL;
+       for (i = 0; strv[i]; i++)
+               strv[i] = g_strdup (strv[i]);
+
+       return (char **) strv;
+}
+
+/*****************************************************************************/
+
+gssize
+nm_utils_ptrarray_find_binary_search (gconstpointer *list,
+                                      gsize len,
+                                      gconstpointer needle,
+                                      GCompareDataFunc cmpfcn,
+                                      gpointer user_data,
+                                      gssize *out_idx_first,
+                                      gssize *out_idx_last)
+{
+       gssize imin, imax, imid, i2min, i2max, i2mid;
+       int cmp;
+
+       g_return_val_if_fail (list || !len, ~((gssize) 0));
+       g_return_val_if_fail (cmpfcn, ~((gssize) 0));
+
+       imin = 0;
+       if (len > 0) {
+               imax = len - 1;
+
+               while (imin <= imax) {
+                       imid = imin + (imax - imin) / 2;
+
+                       cmp = cmpfcn (list[imid], needle, user_data);
+                       if (cmp == 0) {
+                               /* we found a matching entry at index imid.
+                                *
+                                * Does the caller request the first/last index as well (in case that
+                                * there are multiple entries which compare equal). */
+
+                               if (out_idx_first) {
+                                       i2min = imin;
+                                       i2max = imid + 1;
+                                       while (i2min <= i2max) {
+                                               i2mid = i2min + (i2max - i2min) / 2;
+
+                                               cmp = cmpfcn (list[i2mid], needle, user_data);
+                                               if (cmp == 0)
+                                                       i2max = i2mid -1;
+                                               else {
+                                                       nm_assert (cmp < 0);
+                                                       i2min = i2mid + 1;
+                                               }
+                                       }
+                                       *out_idx_first = i2min;
+                               }
+                               if (out_idx_last) {
+                                       i2min = imid + 1;
+                                       i2max = imax;
+                                       while (i2min <= i2max) {
+                                               i2mid = i2min + (i2max - i2min) / 2;
+
+                                               cmp = cmpfcn (list[i2mid], needle, user_data);
+                                               if (cmp == 0)
+                                                       i2min = i2mid + 1;
+                                               else {
+                                                       nm_assert (cmp > 0);
+                                                       i2max = i2mid - 1;
+                                               }
+                                       }
+                                       *out_idx_last = i2min - 1;
+                               }
+                               return imid;
+                       }
+
+                       if (cmp < 0)
+                               imin = imid + 1;
+                       else
+                               imax = imid - 1;
+               }
+       }
+
+       /* return the inverse of @imin. This is a negative number, but
+        * also is ~imin the position where the value should be inserted. */
+       imin = ~imin;
+       NM_SET_OUT (out_idx_first, imin);
+       NM_SET_OUT (out_idx_last, imin);
+       return imin;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_array_find_binary_search:
+ * @list: the list to search. It must be sorted according to @cmpfcn ordering.
+ * @elem_size: the size in bytes of each element in the list
+ * @len: the number of elements in @list
+ * @needle: the value that is searched
+ * @cmpfcn: the compare function. The elements @list are passed as first
+ *   argument to @cmpfcn, while @needle is passed as second. Usually, the
+ *   needle is the same data type as inside the list, however, that is
+ *   not necessary, as long as @cmpfcn takes care to cast the two arguments
+ *   accordingly.
+ * @user_data: optional argument passed to @cmpfcn
+ *
+ * Performs binary search for @needle in @list. On success, returns the
+ * (non-negative) index where the compare function found the searched element.
+ * On success, it returns a negative value. Note that the return negative value
+ * is the bitwise inverse of the position where the element should be inserted.
+ *
+ * If the list contains multiple matching elements, an arbitrary index is
+ * returned.
+ *
+ * Returns: the index to the element in the list, or the (negative, bitwise inverted)
+ *   position where it should be.
+ */
+gssize
+nm_utils_array_find_binary_search (gconstpointer list,
+                                   gsize elem_size,
+                                   gsize len,
+                                   gconstpointer needle,
+                                   GCompareDataFunc cmpfcn,
+                                   gpointer user_data)
+{
+       gssize imin, imax, imid;
+       int cmp;
+
+       g_return_val_if_fail (list || !len, ~((gssize) 0));
+       g_return_val_if_fail (cmpfcn, ~((gssize) 0));
+       g_return_val_if_fail (elem_size > 0, ~((gssize) 0));
+
+       imin = 0;
+       if (len == 0)
+               return ~imin;
+
+       imax = len - 1;
+
+       while (imin <= imax) {
+               imid = imin + (imax - imin) / 2;
+
+               cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data);
+               if (cmp == 0)
+                       return imid;
+
+               if (cmp < 0)
+                       imin = imid + 1;
+               else
+                       imax = imid - 1;
+       }
+
+       /* return the inverse of @imin. This is a negative number, but
+        * also is ~imin the position where the value should be inserted. */
+       return ~imin;
+}
+
+/*****************************************************************************/
+
+/**
+ * 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`.
+ * @out_ppid: parent process id
+ *
+ * Originally copied from polkit source (src/polkit/polkitunixprocess.c)
+ * and adjusted.
+ *
+ * Returns: the timestamp when the process started (by parsing /proc/$PID/stat).
+ * If an error occurs (e.g. the process does not exist), 0 is returned.
+ *
+ * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds)
+ **/
+guint64
+nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
+{
+       guint64 start_time;
+       char filename[256];
+       gs_free char *contents = NULL;
+       size_t length;
+       gs_free const char **tokens = NULL;
+       char *p;
+       char state = ' ';
+       gint64 ppid = 0;
+
+       start_time = 0;
+       contents = NULL;
+
+       g_return_val_if_fail (pid > 0, 0);
+
+       nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+
+       if (!g_file_get_contents (filename, &contents, &length, NULL))
+               goto fail;
+
+       /* start time is the token at index 19 after the '(process name)' entry - since only this
+        * field can contain the ')' character, search backwards for this to avoid malicious
+        * processes trying to fool us
+        */
+       p = strrchr (contents, ')');
+       if (!p)
+               goto fail;
+       p += 2; /* skip ') ' */
+       if (p - contents >= (int) length)
+               goto fail;
+
+       state = p[0];
+
+       tokens = nm_utils_strsplit_set (p, " ", FALSE);
+
+       if (NM_PTRARRAY_LEN (tokens) < 20)
+               goto fail;
+
+       if (out_ppid) {
+               ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
+               if (ppid == 0)
+                       goto fail;
+       }
+
+       start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0);
+       if (start_time == 0)
+               goto fail;
+
+       NM_SET_OUT (out_state, state);
+       NM_SET_OUT (out_ppid, ppid);
+       return start_time;
+
+fail:
+       NM_SET_OUT (out_state, ' ');
+       NM_SET_OUT (out_ppid, 0);
+       return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * _nm_utils_strv_sort:
+ * @strv: pointer containing strings that will be sorted
+ *   in-place, %NULL is allowed, unless @len indicates
+ *   that there are more elements.
+ * @len: the number of elements in strv. If negative,
+ *   strv must be a NULL terminated array and the length
+ *   will be calculated first. If @len is a positive
+ *   number, all first @len elements in @strv must be
+ *   non-NULL, valid strings.
+ *
+ * Ascending sort of the array @strv inplace, using plain strcmp() string
+ * comparison.
+ */
+void
+_nm_utils_strv_sort (const char **strv, gssize len)
+{
+       gsize l;
+
+       l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len;
+
+       if (l <= 1)
+               return;
+
+       nm_assert (l <= (gsize) G_MAXINT);
+
+       g_qsort_with_data (strv,
+                          l,
+                          sizeof (const char *),
+                          nm_strcmp_p_with_data,
+                          NULL);
+}
+
+/*****************************************************************************/
+
+gpointer
+_nm_utils_user_data_pack (int nargs, gconstpointer *args)
+{
+       int i;
+       gpointer *data;
+
+       nm_assert (nargs > 0);
+       nm_assert (args);
+
+       data = g_slice_alloc (((gsize) nargs) * sizeof (gconstpointer));
+       for (i = 0; i < nargs; i++)
+               data[i] = (gpointer) args[i];
+       return data;
+}
+
+void
+_nm_utils_user_data_unpack (gpointer user_data, int nargs, ...)
+{
+       gpointer *data = user_data;
+       va_list ap;
+       int i;
+
+       nm_assert (data);
+       nm_assert (nargs > 0);
+
+       va_start (ap, nargs);
+       for (i = 0; i < nargs; i++) {
+               gpointer *dst;
+
+               dst = va_arg (ap, gpointer *);
+               nm_assert (dst);
+
+               *dst = data[i];
+       }
+       va_end (ap);
+
+       g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data);
+}
+
+/*****************************************************************************/
+
+#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t')
+
+const char *
+_nm_utils_escape_spaces (const char *str, char **to_free)
+{
+       const char *ptr = str;
+       char *ret, *r;
+
+       *to_free = NULL;
+
+       if (!str)
+               return NULL;
+
+       while (TRUE) {
+               if (!*ptr)
+                       return str;
+               if (IS_SPACE (*ptr))
+                       break;
+               ptr++;
+       }
+
+       ptr = str;
+       ret = g_new (char, strlen (str) * 2 + 1);
+       r = ret;
+       *to_free = ret;
+       while (*ptr) {
+               if (IS_SPACE (*ptr))
+                       *r++ = '\\';
+               *r++ = *ptr++;
+       }
+       *r = '\0';
+
+       return ret;
+}
+
+char *
+_nm_utils_unescape_spaces (char *str)
+{
+       guint i, j = 0;
+
+       if (!str)
+               return NULL;
+
+       for (i = 0; str[i]; i++) {
+               if (str[i] == '\\' && IS_SPACE (str[i+1]))
+                       i++;
+               str[j++] = str[i];
+       }
+       str[j] = '\0';
+
+       return str;
+}
+
+#undef IS_SPACE
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index f1f9f51..b309973 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -22,8 +22,225 @@
 #ifndef __NM_SHARED_UTILS_H__
 #define __NM_SHARED_UTILS_H__
 
+#include <netinet/in.h>
+
 /*****************************************************************************/
 
+static inline gboolean
+_NM_INT_NOT_NEGATIVE (gssize val)
+{
+       /* whether an enum (without negative values) is a signed int, depends on compiler options
+        * and compiler implementation.
+        *
+        * When using such an enum for accessing an array, one naturally wants to check
+        * that the enum is not negative. However, the compiler doesn't like a plain
+        * comparisong "enum_val >= 0", because (if the enum is unsigned), it will warn
+        * that the expression is always true *duh*. Not even a cast to a signed
+        * type helps to avoid the compiler warning in any case.
+        *
+        * The sole purpose of this function is to avoid a compiler warning, when checking
+        * that an enum is not negative. */
+       return val >= 0;
+}
+
+/*****************************************************************************/
+
+static inline char
+nm_utils_addr_family_to_char (int addr_family)
+{
+       switch (addr_family) {
+       case AF_INET:  return '4';
+       case AF_INET6: return '6';
+       }
+       g_return_val_if_reached ('?');
+}
+
+static inline gsize
+nm_utils_addr_family_to_size (int addr_family)
+{
+       switch (addr_family) {
+       case AF_INET:  return sizeof (in_addr_t);
+       case AF_INET6: return sizeof (struct in6_addr);
+       }
+       g_return_val_if_reached (0);
+}
+
+#define nm_assert_addr_family(addr_family) \
+       nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6))
+
+/*****************************************************************************/
+
+typedef struct {
+       union {
+               guint8 addr_ptr[1];
+               in_addr_t addr4;
+               struct in6_addr addr6;
+
+               /* NMIPAddr is really a union for IP addresses.
+                * However, as ethernet addresses fit in here nicely, use
+                * it also for an ethernet MAC address. */
+               guint8 addr_eth[6 /*ETH_ALEN*/];
+       };
+} NMIPAddr;
+
+extern const NMIPAddr nm_ip_addr_zero;
+
+static inline void
+nm_ip_addr_set (int addr_family, gpointer dst, const NMIPAddr *src)
+{
+       nm_assert_addr_family (addr_family);
+       nm_assert (dst);
+       nm_assert (src);
+
+       if (addr_family != AF_INET6)
+               *((in_addr_t *) dst) = src->addr4;
+       else
+               *((struct in6_addr *) dst) = src->addr6;
+}
+
+/*****************************************************************************/
+
+#define NM_CMP_RETURN(c) \
+    G_STMT_START { \
+        const int _cc = (c); \
+        if (_cc) \
+            return _cc < 0 ? -1 : 1; \
+    } G_STMT_END
+
+#define NM_CMP_SELF(a, b) \
+    G_STMT_START { \
+        typeof (a) _a = (a); \
+        typeof (b) _b = (b); \
+        \
+        if (_a == _b) \
+            return 0; \
+        if (!_a) \
+            return -1; \
+        if (!_b) \
+            return 1; \
+    } G_STMT_END
+
+#define NM_CMP_DIRECT(a, b) \
+    G_STMT_START { \
+        typeof (a) _a = (a); \
+        typeof (b) _b = (b); \
+        \
+        if (_a != _b) \
+            return (_a < _b) ? -1 : 1; \
+    } G_STMT_END
+
+#define NM_CMP_DIRECT_MEMCMP(a, b, size) \
+    NM_CMP_RETURN (memcmp ((a), (b), (size)))
+
+#define NM_CMP_DIRECT_STRCMP0(a, b) \
+    NM_CMP_RETURN (g_strcmp0 ((a), (b)))
+
+#define NM_CMP_DIRECT_IN6ADDR(a, b) \
+    G_STMT_START { \
+        const struct in6_addr *const _a = (a); \
+        const struct in6_addr *const _b = (b); \
+        NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \
+    } G_STMT_END
+
+#define NM_CMP_FIELD(a, b, field) \
+    NM_CMP_DIRECT (((a)->field), ((b)->field))
+
+#define NM_CMP_FIELD_UNSAFE(a, b, field) \
+    G_STMT_START { \
+        /* it's unsafe, because it evaluates the arguments more then once.
+         * This is necessary for bitfields, for which typeof() doesn't work. */ \
+        if (((a)->field) != ((b)->field)) \
+            return ((a)->field < ((b)->field)) ? -1 : 1; \
+    } G_STMT_END
+
+#define NM_CMP_FIELD_BOOL(a, b, field) \
+    NM_CMP_DIRECT (!!((a)->field), !!((b)->field))
+
+#define NM_CMP_FIELD_STR(a, b, field) \
+    NM_CMP_RETURN (strcmp (((a)->field), ((b)->field)))
+
+#define NM_CMP_FIELD_STR_INTERNED(a, b, field) \
+    G_STMT_START { \
+        const char *_a = ((a)->field); \
+        const char *_b = ((b)->field); \
+        \
+        if (_a != _b) { \
+            NM_CMP_RETURN (g_strcmp0 (_a, _b)); \
+        } \
+    } G_STMT_END
+
+#define NM_CMP_FIELD_STR0(a, b, field) \
+    NM_CMP_RETURN (g_strcmp0 (((a)->field), ((b)->field)))
+
+#define NM_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \
+    NM_CMP_RETURN (memcmp (&((a)->field), &((b)->field), \
+                           MIN (len, sizeof ((a)->field))))
+
+#define NM_CMP_FIELD_MEMCMP(a, b, field) \
+    NM_CMP_RETURN (memcmp (&((a)->field), \
+                           &((b)->field), \
+                           sizeof ((a)->field)))
+
+#define NM_CMP_FIELD_IN6ADDR(a, b, field) \
+    G_STMT_START { \
+        const struct in6_addr *const _a = &((a)->field); \
+        const struct in6_addr *const _b = &((b)->field); \
+        NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \
+    } G_STMT_END
+
+/*****************************************************************************/
+
+static inline gboolean
+nm_utils_mem_all_zero (gconstpointer mem, gsize len)
+{
+       const guint8 *p;
+
+       for (p = mem; len-- > 0; p++) {
+               if (*p != 0)
+                       return FALSE;
+       }
+
+       /* incidentally, a buffer with len==0, is also *all-zero*. */
+       return TRUE;
+}
+
+/*****************************************************************************/
+
+/* like g_memdup(). The difference is that the @size argument is of type
+ * gsize, while g_memdup() has type guint. Since, the size of container types
+ * like GArray is guint as well, this means trying to g_memdup() an
+ * array,
+ *    g_memdup (array->data, array->len * sizeof (ElementType))
+ * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType)
+ * bytes. That seems unnecessarily dangerous to me.
+ * nm_memdup() avoids that, because its size argument is always large enough
+ * to contain all data that a GArray can hold.
+ *
+ * Another minor difference to g_memdup() is that the glib version also
+ * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1)
+ * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that
+ * is desirable, because @size MUST be correct at all times. @size
+ * may be zero, but one must not claim to have non-zero bytes when
+ * passing a %NULL @data pointer.
+ */
+static inline gpointer
+nm_memdup (gconstpointer data, gsize size)
+{
+       gpointer p;
+
+       if (size == 0)
+               return NULL;
+       p = g_malloc (size);
+       memcpy (p, data, size);
+       return p;
+}
+
+/*****************************************************************************/
+
+extern const void *const _NM_PTRARRAY_EMPTY[1];
+
+#define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY)
+
 static inline void
 _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
 {
@@ -40,17 +257,226 @@ _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
 void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
 void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
 void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
+void nm_utils_strbuf_seek_end (char **buf, gsize *len);
+
+const char *nm_strquote (char *buf, gsize buf_len, const char *str);
+
+static inline gboolean
+nm_utils_is_separator (const char c)
+{
+       return NM_IN_SET (c, ' ', '\t');
+}
+
+/*****************************************************************************/
+
+static inline gboolean
+nm_gbytes_equal0 (GBytes *a, GBytes *b)
+{
+       return a == b || (a && b && g_bytes_equal (a, b));
+}
+
+gboolean nm_utils_gbytes_equal_mem (GBytes *bytes,
+                                    gconstpointer mem_data,
+                                    gsize mem_len);
+
+GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes);
+
+/*****************************************************************************/
+
+static inline int
+nm_utils_hexchar_to_int (char ch)
+{
+       G_STATIC_ASSERT_EXPR ('0' < 'A');
+       G_STATIC_ASSERT_EXPR ('A' < 'a');
+
+       if (ch >= '0') {
+               if (ch <= '9')
+                       return ch - '0';
+               if (ch >= 'A') {
+                       if (ch <= 'F')
+                               return ((int) ch) + (10 - (int) 'A');
+                       if (ch >= 'a' && ch <= 'f')
+                               return ((int) ch) + (10 - (int) 'a');
+               }
+       }
+       return -1;
+}
+
+/*****************************************************************************/
+
+const char *nm_utils_dbus_path_get_last_component (const char *dbus_path);
+
+int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
 
 /*****************************************************************************/
 
+const char **nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping);
+
 gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
 
+char **_nm_utils_strv_cleanup (char **strv,
+                               gboolean strip_whitespace,
+                               gboolean skip_empty,
+                               gboolean skip_repeated);
+
+/*****************************************************************************/
+
+guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix);
+guint32 _nm_utils_ip4_get_default_prefix (guint32 ip);
+
+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,
+                                     gpointer out_addr);
+
+gboolean nm_utils_parse_inaddr (int addr_family,
+                                const char *text,
+                                char **out_addr);
+
+gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family,
+                                           const char *text,
+                                           gpointer out_addr,
+                                           int *out_prefix);
+
+gboolean nm_utils_parse_inaddr_prefix (int addr_family,
+                                       const char *text,
+                                       char **out_addr,
+                                       int *out_prefix);
+
 gint64 _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback);
 
-gint _nm_utils_ascii_str_to_bool (const char *str,
-                                  gint default_value);
+int _nm_utils_ascii_str_to_bool (const char *str,
+                                  int default_value);
+
+/*****************************************************************************/
+
+extern char _nm_utils_to_string_buffer[2096];
+
+void     nm_utils_to_string_buffer_init (char **buf, gsize *len);
+gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len);
+
+/*****************************************************************************/
+
+typedef struct {
+       unsigned flag;
+       const char *name;
+} NMUtilsFlags2StrDesc;
+
+#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, }
+
+#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \
+scope const char * \
+fcn_name (flags_type flags, char *buf, gsize len) \
+{ \
+       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); \
+};
+
+#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \
+       _NM_UTILS_FLAGS2STR_DEFINE (, fcn_name, flags_type, __VA_ARGS__)
+#define NM_UTILS_FLAGS2STR_DEFINE_STATIC(fcn_name, flags_type, ...) \
+       _NM_UTILS_FLAGS2STR_DEFINE (static, fcn_name, flags_type, __VA_ARGS__)
+
+const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
+                                gsize n_descs,
+                                unsigned flags,
+                                char *buf,
+                                gsize len);
+
+/*****************************************************************************/
+
+#define NM_UTILS_ENUM2STR(v, n)     (void) 0; case v: s = ""n""; break; (void) 0
+#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0
+
+#define _NM_UTILS_ENUM2STR_DEFINE(scope, fcn_name, lookup_type, int_fmt, ...) \
+scope const char * \
+fcn_name (lookup_type val, char *buf, gsize len) \
+{ \
+       nm_utils_to_string_buffer_init (&buf, &len); \
+       if (len) { \
+               const char *s = NULL; \
+               switch (val) { \
+                       (void) 0, \
+                       __VA_ARGS__ \
+                       (void) 0; \
+               }; \
+               if (s) \
+                       g_strlcpy (buf, s, len); \
+               else \
+                       g_snprintf (buf, len, "(%"int_fmt")", val); \
+       } \
+       return buf; \
+}
+
+#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \
+       _NM_UTILS_ENUM2STR_DEFINE (, fcn_name, lookup_type, "d", __VA_ARGS__)
+#define NM_UTILS_ENUM2STR_DEFINE_STATIC(fcn_name, lookup_type, ...) \
+       _NM_UTILS_ENUM2STR_DEFINE (static, fcn_name, lookup_type, "d", __VA_ARGS__)
+
+/*****************************************************************************/
+
+#define _nm_g_slice_free_fcn_define(mem_size) \
+static inline void \
+_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \
+{ \
+       g_slice_free1 (mem_size, mem_block); \
+}
+
+_nm_g_slice_free_fcn_define (1)
+_nm_g_slice_free_fcn_define (2)
+_nm_g_slice_free_fcn_define (4)
+_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)
+
+#define _nm_g_slice_free_fcn1(mem_size) \
+       ({ \
+               void (*_fcn) (gpointer); \
+               \
+               /* If mem_size is a compile time constant, the compiler
+                * will be able to optimize this. Hence, you don't want
+                * to call this with a non-constant size argument. */ \
+               G_STATIC_ASSERT_EXPR (   ((mem_size) ==  1) \
+                                     || ((mem_size) ==  2) \
+                                     || ((mem_size) ==  4) \
+                                     || ((mem_size) ==  8) \
+                                     || ((mem_size) == 10) \
+                                     || ((mem_size) == 12) \
+                                     || ((mem_size) == 16)); \
+               switch ((mem_size)) { \
+               case  1: _fcn = _nm_g_slice_free_fcn_1;  break; \
+               case  2: _fcn = _nm_g_slice_free_fcn_2;  break; \
+               case  4: _fcn = _nm_g_slice_free_fcn_4;  break; \
+               case  8: _fcn = _nm_g_slice_free_fcn_8;  break; \
+               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; \
+               default: g_assert_not_reached (); _fcn = NULL; break; \
+               } \
+               _fcn; \
+       })
+
+/**
+ * nm_g_slice_free_fcn:
+ * @type: type argument for sizeof() operator that you would
+ *   pass to g_slice_new().
+ *
+ * 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)))
+
+#define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64))
 
 /*****************************************************************************/
 
@@ -62,10 +488,40 @@ gint _nm_utils_ascii_str_to_bool (const char *str,
  *   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_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular
+ *   purpose during nm_device_check_connection_compatible() to indicate that
+ *   the profile does not match the device already because their type differs.
+ *   That is, there is a fundamental reason of trying to check a profile that
+ *   cannot possibly match on this device.
+ * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE: used for a very particular
+ *   purpose during nm_device_check_connection_available(), to indicate that the
+ *   device is not available because it is unmanaged.
+ * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY: the profile is currently not
+ *   available/compatible with the device, but this may be only temporary.
+ *
+ * @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument.
  */
 typedef enum {
        NM_UTILS_ERROR_UNKNOWN = 0,                 /*< nick=Unknown >*/
        NM_UTILS_ERROR_CANCELLED_DISPOSING,         /*< nick=CancelledDisposing >*/
+       NM_UTILS_ERROR_INVALID_ARGUMENT,            /*< nick=InvalidArgument >*/
+
+       /* the following codes have a special meaning and are exactly used for
+        * nm_device_check_connection_compatible() and nm_device_check_connection_available().
+        *
+        * Actually, their meaning is not very important (so, don't think too
+        * hard about the name of these error codes). What is important, is their
+        * relative order (i.e. the integer value of the codes). When manager
+        * searches for a suitable device, it will check all devices whether
+        * a profile can be activated. If they all fail, it will pick the error
+        * message from the device that returned the *highest* error code,
+        * in the hope that this message makes the most sense for the caller.
+        * */
+       NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+       NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
+       NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+
 } NMUtilsError;
 
 #define NM_UTILS_ERROR (nm_utils_error_quark ())
@@ -77,13 +533,298 @@ void nm_utils_error_set_cancelled (GError **error,
 gboolean nm_utils_error_is_cancelled (GError *error,
                                       gboolean consider_is_disposing);
 
+static inline void
+nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
+{
+       g_set_error_literal (error, NM_UTILS_ERROR, error_code, literal);
+}
+
+#define nm_utils_error_set(error, error_code, ...) \
+       g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__)
+
 /*****************************************************************************/
 
 gboolean nm_g_object_set_property (GObject *object,
-                                   const gchar  *property_name,
+                                   const char   *property_name,
                                    const GValue *value,
                                    GError **error);
 
+gboolean nm_g_object_set_property_boolean (GObject *object,
+                                           const char   *property_name,
+                                           gboolean value,
+                                           GError **error);
+
+gboolean nm_g_object_set_property_uint (GObject *object,
+                                        const char   *property_name,
+                                        guint value,
+                                        GError **error);
+
+GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype,
+                                                        const char *property_name);
+
+/*****************************************************************************/
+
+typedef enum {
+       NM_UTILS_STR_UTF8_SAFE_FLAG_NONE                = 0,
+       NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL         = 0x0001,
+       NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII    = 0x0002,
+} NMUtilsStrUtf8SafeFlags;
+
+const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, 
char **to_free);
+const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char 
**to_free);
+gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free);
+
+const char *nm_utils_str_utf8safe_escape   (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free);
+const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free);
+
+char *nm_utils_str_utf8safe_escape_cp   (const char *str, NMUtilsStrUtf8SafeFlags flags);
+char *nm_utils_str_utf8safe_unescape_cp (const char *str);
+
+char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags);
+
+static inline void
+nm_g_variant_unref_floating (GVariant *var)
+{
+       /* often a function wants to keep a reference to an input variant.
+        * It uses g_variant_ref_sink() to either increase the ref-count,
+        * or take ownership of a possibly floating reference.
+        *
+        * If the function doesn't actually want to do anything with the
+        * input variant, it still must make sure that a passed in floating
+        * reference is consumed. Hence, this helper which:
+        *
+        *   - does nothing if @var is not floating
+        *   - unrefs (consumes) @var if it is floating. */
+       if (g_variant_is_floating (var))
+               g_variant_unref (var);
+}
+
+/*****************************************************************************/
+
+static inline int
+nm_utf8_collate0 (const char *a, const char *b)
+{
+       if (!a)
+               return !b ? 0 : -1;
+       if (!b)
+               return 1;
+       return g_utf8_collate (a, b);
+}
+
+int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data);
+int nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
+int nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
+
+/*****************************************************************************/
+
+typedef struct {
+       const char *name;
+} NMUtilsNamedEntry;
+
+typedef struct {
+       union {
+               NMUtilsNamedEntry named_entry;
+               const char *name;
+       };
+       union {
+               const char *value_str;
+               gconstpointer value_ptr;
+       };
+} NMUtilsNamedValue;
+
+#define nm_utils_named_entry_cmp           nm_strcmp_p
+#define nm_utils_named_entry_cmp_with_data nm_strcmp_p_with_data
+
+NMUtilsNamedValue *nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len);
+
+gpointer *nm_utils_hash_keys_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,
+                           guint *out_length)
+{
+       return (const char **) nm_utils_hash_keys_to_array ((GHashTable *) hash,
+                                                           sorted ? nm_strcmp_p_with_data : NULL,
+                                                           NULL,
+                                                           out_length);
+}
+
+char **nm_utils_strv_make_deep_copied (const char **strv);
+
+static inline char **
+nm_utils_strv_make_deep_copied_nonnull (const char **strv)
+{
+       return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1);
+}
+
+/*****************************************************************************/
+
+gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list,
+                                             gsize len,
+                                             gconstpointer needle,
+                                             GCompareDataFunc cmpfcn,
+                                             gpointer user_data,
+                                             gssize *out_idx_first,
+                                             gssize *out_idx_last);
+
+gssize nm_utils_array_find_binary_search (gconstpointer list,
+                                          gsize elem_size,
+                                          gsize len,
+                                          gconstpointer needle,
+                                          GCompareDataFunc cmpfcn,
+                                          gpointer user_data);
+
+/*****************************************************************************/
+
+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)
+
+/*****************************************************************************/
+
+#define NM_UTILS_NS_PER_SECOND   ((gint64) 1000000000)
+#define NM_UTILS_NS_PER_MSEC     ((gint64) 1000000)
+#define NM_UTILS_MSEC_PER_SECOND ((gint64) 1000)
+#define NM_UTILS_NS_TO_MSEC_CEIL(nsec)      (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC)
+
+/*****************************************************************************/
+
+int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns);
+ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll);
+int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll);
+
+/*****************************************************************************/
+
+static inline const char *
+nm_utils_dbus_normalize_object_path (const char *path)
+{
+       /* D-Bus does not allow an empty object path. Hence, whenever we mean NULL / no-object
+        * on D-Bus, it's path is actually "/".
+        *
+        * Normalize that away, and return %NULL in that case. */
+       if (path && path[0] == '/' && path[1] == '\0')
+               return NULL;
+       return path;
+}
+
+#define NM_DEFINE_GDBUS_ARG_INFO_FULL(name_, ...) \
+       ((GDBusArgInfo *) (&((const GDBusArgInfo) { \
+               .ref_count = -1, \
+               .name = name_, \
+               __VA_ARGS__ \
+       })))
+
+#define NM_DEFINE_GDBUS_ARG_INFO(name_, a_signature) \
+       NM_DEFINE_GDBUS_ARG_INFO_FULL ( \
+               name_, \
+               .signature = a_signature, \
+       )
+
+#define NM_DEFINE_GDBUS_ARG_INFOS(...) \
+       ((GDBusArgInfo **) ((const GDBusArgInfo *[]) { \
+               __VA_ARGS__ \
+               NULL, \
+       }))
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFO(name_, ...) \
+       ((GDBusPropertyInfo *) (&((const GDBusPropertyInfo) { \
+               .ref_count = -1, \
+               .name = name_, \
+               __VA_ARGS__ \
+       })))
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFO_READABLE(name_, m_signature) \
+       NM_DEFINE_GDBUS_PROPERTY_INFO ( \
+               name_, \
+               .signature = m_signature, \
+               .flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \
+       )
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFOS(...) \
+       ((GDBusPropertyInfo **) ((const GDBusPropertyInfo *[]) { \
+               __VA_ARGS__ \
+               NULL, \
+       }))
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(name_, ...) \
+       { \
+               .ref_count = -1, \
+               .name = name_, \
+               __VA_ARGS__ \
+       }
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFO(name_, ...) \
+       ((GDBusSignalInfo *) (&((const GDBusSignalInfo) NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (name_, 
__VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFOS(...) \
+       ((GDBusSignalInfo **) ((const GDBusSignalInfo *[]) { \
+               __VA_ARGS__ \
+               NULL, \
+       }))
+
+#define NM_DEFINE_GDBUS_METHOD_INFO_INIT(name_, ...) \
+       { \
+               .ref_count = -1, \
+               .name = name_, \
+               __VA_ARGS__ \
+       }
+
+#define NM_DEFINE_GDBUS_METHOD_INFO(name_, ...) \
+       ((GDBusMethodInfo *) (&((const GDBusMethodInfo) NM_DEFINE_GDBUS_METHOD_INFO_INIT (name_, 
__VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_METHOD_INFOS(...) \
+       ((GDBusMethodInfo **) ((const GDBusMethodInfo *[]) { \
+               __VA_ARGS__ \
+               NULL, \
+       }))
+
+#define NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(name_, ...) \
+       { \
+               .ref_count = -1, \
+               .name = name_, \
+               __VA_ARGS__ \
+       }
+
+#define NM_DEFINE_GDBUS_INTERFACE_INFO(name_, ...) \
+       ((GDBusInterfaceInfo *) (&((const GDBusInterfaceInfo) NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (name_, 
__VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_INTERFACE_VTABLE(...) \
+       ((GDBusInterfaceVTable *) (&((const GDBusInterfaceVTable) { \
+               __VA_ARGS__ \
+       })))
+
 /*****************************************************************************/
 
+guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
+
+/*****************************************************************************/
+
+gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args);
+
+#define nm_utils_user_data_pack(...) \
+       _nm_utils_user_data_pack(NM_NARG (__VA_ARGS__), (gconstpointer[]) { __VA_ARGS__ })
+
+void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...);
+
+#define nm_utils_user_data_unpack(user_data, ...) \
+       _nm_utils_user_data_unpack(user_data, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+
+/*****************************************************************************/
+
+const char *_nm_utils_escape_spaces (const char *str, char **to_free);
+char *_nm_utils_unescape_spaces (char *str);
+
 #endif /* __NM_SHARED_UTILS_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]