about summary refs log tree commit diff
path: root/shared/nm-glib-aux/nm-shared-utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-glib-aux/nm-shared-utils.h')
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h495
1 files changed, 457 insertions, 38 deletions
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index 740e61d0..537b402b 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -115,11 +115,31 @@ typedef struct {
 		 * However, as ethernet addresses fit in here nicely, use
 		 * it also for an ethernet MAC address. */
 		guint8 addr_eth[6 /*ETH_ALEN*/];
+
+		guint8 array[sizeof (struct in6_addr)];
 	};
 } NMIPAddr;
 
+#define NM_IP_ADDR_INIT { .array = { 0 } }
+
 extern const NMIPAddr nm_ip_addr_zero;
 
+static inline int
+nm_ip_addr_cmp (int addr_family, gconstpointer a, gconstpointer b)
+{
+	nm_assert_addr_family (addr_family);
+	nm_assert (a);
+	nm_assert (b);
+
+	return memcmp (a, b, nm_utils_addr_family_to_size (addr_family));
+}
+
+static inline gboolean
+nm_ip_addr_equal (int addr_family, gconstpointer a, gconstpointer b)
+{
+	return nm_ip_addr_cmp (addr_family, a, b) == 0;
+}
+
 static inline gboolean
 nm_ip_addr_is_null (int addr_family, gconstpointer addr)
 {
@@ -158,6 +178,72 @@ nm_ip4_addr_is_localhost (in_addr_t addr4)
 
 /*****************************************************************************/
 
+#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
+
+static inline const char *
+nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst)
+{
+	const char *s;
+
+	const char *inet_ntop (int af,
+	                       const void *src,
+	                       char *dst,
+	                       socklen_t size);
+
+	nm_assert_addr_family (addr_family);
+	nm_assert (addr);
+	nm_assert (dst);
+
+	s = inet_ntop (addr_family,
+	               addr,
+	               dst,
+	               addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN);
+	nm_assert (s);
+	return s;
+}
+
+static inline const char *
+_nm_utils_inet4_ntop (in_addr_t addr, char dst[static INET_ADDRSTRLEN])
+{
+	return nm_utils_inet_ntop (AF_INET, &addr, dst);
+}
+
+static inline const char *
+_nm_utils_inet6_ntop (const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN])
+{
+	return nm_utils_inet_ntop (AF_INET6, addr, dst);
+}
+
+static inline char *
+nm_utils_inet_ntop_dup (int addr_family, gconstpointer addr)
+{
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	return g_strdup (nm_utils_inet_ntop (addr_family, addr, buf));
+}
+
+static inline char *
+nm_utils_inet4_ntop_dup (in_addr_t addr)
+{
+	return nm_utils_inet_ntop_dup (AF_INET, &addr);
+}
+
+static inline char *
+nm_utils_inet6_ntop_dup (const struct in6_addr *addr)
+{
+	return nm_utils_inet_ntop_dup (AF_INET6, addr);
+}
+
+/*****************************************************************************/
+
+gboolean nm_utils_ipaddr_is_valid (int addr_family,
+                                   const char *str_addr);
+
+gboolean nm_utils_ipaddr_is_normalized (int addr_family,
+                                        const char *str_addr);
+
+/*****************************************************************************/
+
 #define NM_CMP_RETURN(c) \
     G_STMT_START { \
         const int _cc = (c); \
@@ -307,6 +393,8 @@ gboolean nm_utils_gbytes_equal_mem (GBytes *bytes,
 
 GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes);
 
+GVariant *nm_utils_strdict_to_variant_ass (GHashTable *strdict);
+
 /*****************************************************************************/
 
 GVariant *nm_utils_gvariant_vardict_filter (GVariant *src,
@@ -352,7 +440,25 @@ int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
 
 typedef enum {
 	NM_UTILS_STRSPLIT_SET_FLAGS_NONE           = 0,
+
+	/* by default, strsplit will coalesce consecutive delimiters and remove
+	 * them from the result. If this flag is present, empty values are preserved
+	 * and returned.
+	 *
+	 * When combined with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, if a value gets
+	 * empty after strstrip(), it also gets removed. */
 	NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY = (1u << 0),
+
+	/* %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING means that 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. In general,
+	 * nm_utils_strsplit_set_full() does not remove any backslash escape characters
+	 * and does no unescaping. It only considers them for skipping to split at
+	 * an escaped delimiter.
+	 *
+	 * If this is combined with (or implied by %NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED), then
+	 * the backslash escapes are removed from the result.
+	 */
 	NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING = (1u << 1),
 
 	/* If flag is set, does the same as g_strstrip() on the returned tokens.
@@ -392,6 +498,7 @@ typedef enum {
 	 * need extra care, and then only if they proceed one of the relevant characters.
 	 */
 	NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED        = (1u << 3),
+
 } NMUtilsStrsplitSetFlags;
 
 const char **nm_utils_strsplit_set_full (const char *str,
@@ -435,9 +542,65 @@ nm_utils_escaped_tokens_split (const char *str,
 	                                   | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
 }
 
-const char *nm_utils_escaped_tokens_escape (const char *str,
-                                            const char *delimiters,
-                                            char **out_to_free);
+typedef enum {
+	NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_NONE                       = 0,
+	NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_SPACES              = (1ull << 0),
+	NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE       = (1ull << 1),
+	NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE      = (1ull << 2),
+
+	/* Backslash characters will be escaped as "\\\\" if they precede another
+	 * character that makes it necessary. Such characters are:
+	 *
+	 *  1) before another '\\' backslash.
+	 *  2) before any delimiter in @delimiters.
+	 *  3) before any delimiter in @delimiters_as_needed.
+	 *  4) before a white space, if ESCAPE_LEADING_SPACE or ESCAPE_TRAILING_SPACE is set.
+	 *  5) before the end of the word
+	 *
+	 * Rule 4) is an extension. It's not immediately clear why with ESCAPE_LEADING_SPACE
+	 * and ESCAPE_TRAILING_SPACE we want *all* backslashes before a white space escaped.
+	 * The reason is, that we obviously want to use ESCAPE_LEADING_SPACE and ESCAPE_TRAILING_SPACE
+	 * in cases, where we later parse the backslash escaped strings back, but allowing to strip
+	 * unescaped white spaces. That means, we want that " a " gets escaped as "\\ a\\ ".
+	 * On the other hand, we also want that " a\\ b " gets escaped as "\\ a\\\\ b\\ ",
+	 * and not "\\ a\\ b\\ ". Because otherwise, the parser would need to treat "\\ "
+	 * differently depending on whether the sequence is at the beginning, end or middle
+	 * of the word.
+	 *
+	 * Rule 5) is also not immediately obvious. When used with ESCAPE_TRAILING_SPACE,
+	 * we clearly want to allow that an escaped word can have arbitrary
+	 * whitespace suffixes. That's why this mode exists. So we must escape "a\\" as
+	 * "a\\\\", so that appending " " does not change the meaning.
+	 * Also without ESCAPE_TRAILING_SPACE, we want in general that we can concatenate
+	 * two escaped words without changing their meaning. If the words would be "a\\"
+	 * and "," (with ',' being a delimiter), then the result must be "a\\\\" and "\\,"
+	 * so that the concatenated word ("a\\\\\\,") is still the same. If we would escape
+	 * them instead as "a\\" + "\\,", then the concatenated word would be "a\\\\," and
+	 * different.
+	 * */
+	NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED = (1ull << 3),
+
+	NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS    = (1ull << 4),
+} NMUtilsEscapedTokensEscapeFlags;
+
+const char *nm_utils_escaped_tokens_escape_full (const char *str,
+                                                 const char *delimiters,
+                                                 const char *delimiters_as_needed,
+                                                 NMUtilsEscapedTokensEscapeFlags flags,
+                                                 char **out_to_free);
+
+static inline const char *
+nm_utils_escaped_tokens_escape (const char *str,
+                                const char *delimiters,
+                                char **out_to_free)
+{
+	return nm_utils_escaped_tokens_escape_full (str,
+	                                            delimiters,
+	                                            NULL,
+	                                              NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS
+	                                            | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE,
+	                                            out_to_free);
+}
 
 static inline GString *
 nm_utils_escaped_tokens_escape_gstr_assert (const char *str,
@@ -489,6 +652,47 @@ nm_utils_escaped_tokens_escape_gstr (const char *str,
 
 /*****************************************************************************/
 
+static inline const char **
+nm_utils_escaped_tokens_options_split_list (const char *str)
+{
+	return nm_utils_strsplit_set_full (str,
+	                                   ",",
+	                                     NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP
+	                                   | NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
+}
+
+void nm_utils_escaped_tokens_options_split (char *str,
+                                            const char **out_key,
+                                            const char **out_val);
+
+static inline const char *
+nm_utils_escaped_tokens_options_escape_key (const char *key,
+                                            char **out_to_free)
+{
+	return nm_utils_escaped_tokens_escape_full (key,
+	                                            ",=",
+	                                            NULL,
+	                                              NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED
+	                                            | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE
+	                                            | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE,
+	                                            out_to_free);
+}
+
+static inline const char *
+nm_utils_escaped_tokens_options_escape_val (const char *val,
+                                            char **out_to_free)
+{
+	return nm_utils_escaped_tokens_escape_full (val,
+	                                            ",",
+	                                            "=",
+	                                              NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED
+	                                            | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE
+	                                            | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE,
+	                                            out_to_free);
+}
+
+/*****************************************************************************/
+
 #define NM_UTILS_CHECKSUM_LENGTH_MD5          16
 #define NM_UTILS_CHECKSUM_LENGTH_SHA1         20
 #define NM_UTILS_CHECKSUM_LENGTH_SHA256       32
@@ -571,6 +775,17 @@ gboolean nm_utils_parse_inaddr_prefix (int addr_family,
                                        char **out_addr,
                                        int *out_prefix);
 
+gint64 nm_g_ascii_strtoll (const char *nptr,
+                           char **endptr,
+                           guint base);
+
+guint64 nm_g_ascii_strtoull (const char *nptr,
+                             char **endptr,
+                             guint base);
+
+double nm_g_ascii_strtod (const char *nptr,
+                          char **endptr);
+
 gint64  _nm_utils_ascii_str_to_int64  (const char *str, guint base, gint64  min, gint64  max, gint64  fallback);
 guint64 _nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback);
 
@@ -593,8 +808,8 @@ typedef struct {
 
 #define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, }
 
-#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \
-scope const char * \
+#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \
+const char * \
 fcn_name (flags_type flags, char *buf, gsize len) \
 { \
 	static const NMUtilsFlags2StrDesc descs[] = { \
@@ -604,11 +819,6 @@ fcn_name (flags_type flags, char *buf, gsize len) \
 	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,
@@ -620,8 +830,8 @@ const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
 #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 * \
+#define NM_UTILS_ENUM2STR_DEFINE_FULL(fcn_name, lookup_type, int_fmt, ...) \
+const char * \
 fcn_name (lookup_type val, char *buf, gsize len) \
 { \
 	nm_utils_to_string_buffer_init (&buf, &len); \
@@ -641,9 +851,7 @@ fcn_name (lookup_type val, char *buf, gsize len) \
 }
 
 #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__)
+	NM_UTILS_ENUM2STR_DEFINE_FULL (fcn_name, lookup_type, "d", __VA_ARGS__)
 
 /*****************************************************************************/
 
@@ -797,8 +1005,15 @@ nm_utils_error_new_cancelled (gboolean is_disposing,
 	return error;
 }
 
-gboolean nm_utils_error_is_cancelled (GError *error,
-                                      gboolean consider_is_disposing);
+gboolean nm_utils_error_is_cancelled_or_disposing (GError *error);
+
+static inline gboolean
+nm_utils_error_is_cancelled (GError *error)
+{
+	return    error
+	       && error->code == G_IO_ERROR_CANCELLED
+	       && error->domain == G_IO_ERROR;
+}
 
 gboolean nm_utils_error_is_notfound (GError *error);
 
@@ -927,6 +1142,7 @@ 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,
+	NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET              = 0x0004,
 } NMUtilsStrUtf8SafeFlags;
 
 const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free);
@@ -958,6 +1174,32 @@ nm_g_variant_unref_floating (GVariant *var)
 		g_variant_unref (var);
 }
 
+#define nm_g_variant_lookup(dictionary, ...) \
+	({ \
+		GVariant *const _dictionary = (dictionary); \
+		\
+		(   _dictionary \
+		 && g_variant_lookup (_dictionary, __VA_ARGS__)); \
+	})
+
+static inline GVariant *
+nm_g_variant_lookup_value (GVariant *dictionary,
+                           const char *key,
+                           const GVariantType *expected_type)
+{
+	return   dictionary
+	       ? g_variant_lookup_value (dictionary, key, expected_type)
+	       : NULL;
+}
+
+static inline gboolean
+nm_g_variant_is_of_type (GVariant *value,
+                         const GVariantType *type)
+{
+	return    value
+	       && g_variant_is_of_type (value, type);
+}
+
 static inline void
 nm_g_source_destroy_and_unref (GSource *source)
 {
@@ -986,12 +1228,19 @@ GSource *nm_g_idle_source_new (int priority,
                                gpointer user_data,
                                GDestroyNotify destroy_notify);
 
-GSource *nm_g_timeout_source_new (guint timeout_ms,
+GSource *nm_g_timeout_source_new (guint timeout_msec,
                                   int priority,
                                   GSourceFunc func,
                                   gpointer user_data,
                                   GDestroyNotify destroy_notify);
-
+GSource *nm_g_unix_fd_source_new (int fd,
+                                  GIOCondition io_condition,
+                                  int priority,
+                                  gboolean (*source_func) (int fd,
+                                                           GIOCondition condition,
+                                                           gpointer user_data),
+                                  gpointer user_data,
+                                  GDestroyNotify destroy_notify);
 GSource *nm_g_unix_signal_source_new (int signum,
                                       int priority,
                                       GSourceFunc handler,
@@ -1019,6 +1268,25 @@ nm_g_main_context_push_thread_default (GMainContext *context)
 	return context;
 }
 
+static inline gboolean
+nm_g_main_context_is_thread_default (GMainContext *context)
+{
+	GMainContext *cur_context;
+
+	cur_context = g_main_context_get_thread_default ();
+	if (cur_context == context)
+		return TRUE;
+
+	if (G_UNLIKELY (!cur_context))
+		cur_context = g_main_context_default ();
+	else if (G_UNLIKELY (!context))
+		context = g_main_context_default ();
+	else
+		return FALSE;
+
+	return (cur_context == context);
+}
+
 static inline GMainContext *
 nm_g_main_context_push_thread_default_if_necessary (GMainContext *context)
 {
@@ -1077,7 +1345,18 @@ typedef struct {
 	};
 } NMUtilsNamedValue;
 
-NMUtilsNamedValue *nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len);
+NMUtilsNamedValue *nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash,
+                                                                  guint *out_len,
+                                                                  GCompareDataFunc compare_func,
+                                                                  gpointer user_data);
+
+static inline NMUtilsNamedValue *
+nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
+{
+	G_STATIC_ASSERT (G_STRUCT_OFFSET (NMUtilsNamedValue, name) == 0);
+
+	return nm_utils_named_values_from_str_dict_with_sort (hash, out_len, nm_strcmp_p_with_data, NULL);
+}
 
 gssize nm_utils_named_value_list_find (const NMUtilsNamedValue *arr,
                                        gsize len,
@@ -1146,6 +1425,26 @@ char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator);
 
 /*****************************************************************************/
 
+static inline guint
+nm_g_hash_table_size (GHashTable *hash)
+{
+	return hash ? g_hash_table_size (hash) : 0u;
+}
+
+static inline gpointer
+nm_g_hash_table_lookup (GHashTable *hash, gconstpointer key)
+{
+	return hash ? g_hash_table_lookup (hash, key) : NULL;
+}
+
+static inline gboolean
+nm_g_hash_table_remove (GHashTable *hash, gconstpointer key)
+{
+	return hash ? g_hash_table_remove (hash, key) : FALSE;
+}
+
+/*****************************************************************************/
+
 gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list,
                                              gsize len,
                                              gconstpointer needle,
@@ -1190,31 +1489,25 @@ _nm_utils_strv_equal (char **strv1, char **strv2)
 
 /*****************************************************************************/
 
-#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)
+#define NM_UTILS_NSEC_PER_SEC  ((gint64) 1000000000)
+#define NM_UTILS_USEC_PER_SEC  ((gint64) 1000000)
+#define NM_UTILS_MSEC_PER_SEC  ((gint64) 1000)
+#define NM_UTILS_NSEC_PER_MSEC ((gint64) 1000000)
+
+static inline gint64
+NM_UTILS_NSEC_TO_MSEC_CEIL (gint64 nsec)
+{
+	return (nsec + (NM_UTILS_NSEC_PER_MSEC - 1)) / NM_UTILS_NSEC_PER_MSEC;
+}
 
 /*****************************************************************************/
 
-int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns);
+int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_nsec);
 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, \
@@ -1412,6 +1705,92 @@ guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr,
 
 /*****************************************************************************/
 
+#define _NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(fcn_name, \
+                                             value_type, \
+                                             value_type_result, \
+                                             entry_cmd, \
+                                             unknown_val_cmd, \
+                                             get_operator, \
+                                             ...) \
+value_type_result \
+fcn_name (const char *name) \
+{ \
+	static const struct { \
+		const char *name; \
+		value_type value; \
+	} LIST[] = { \
+		__VA_ARGS__ \
+	}; \
+	\
+	if (NM_MORE_ASSERT_ONCE (5)) { \
+		int i; \
+		\
+		for (i = 0; i < G_N_ELEMENTS (LIST); i++) { \
+			nm_assert (LIST[i].name); \
+			if (i > 0) \
+				nm_assert (strcmp (LIST[i - 1].name, LIST[i].name) < 0); \
+		} \
+	} \
+	\
+	{ entry_cmd; } \
+	\
+	if (G_LIKELY (name)) { \
+		G_STATIC_ASSERT (G_N_ELEMENTS (LIST) > 1); \
+		G_STATIC_ASSERT (G_N_ELEMENTS (LIST) < G_MAXINT / 2 - 10); \
+		int imin = 0; \
+		int imax = (G_N_ELEMENTS (LIST) - 1); \
+		int imid = (G_N_ELEMENTS (LIST) - 1) / 2; \
+		\
+		for (;;) { \
+			const int cmp = strcmp (LIST[imid].name, name); \
+			\
+			if (G_UNLIKELY (cmp == 0)) \
+				return get_operator (LIST[imid].value); \
+			\
+			if (cmp < 0) \
+				imin = imid + 1; \
+			else \
+				imax = imid - 1; \
+			\
+			if (G_UNLIKELY (imin > imax)) \
+				break; \
+			\
+			/* integer overflow cannot happen, because LIST is shorter than G_MAXINT/2. */ \
+			imid = (imin + imax) / 2;\
+		} \
+	} \
+	\
+	{ unknown_val_cmd; } \
+}
+
+#define NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE(fcn_name, \
+                                                   result_type, \
+                                                   entry_cmd, \
+                                                   unknown_val_cmd, \
+                                                   ...) \
+	_NM_UTILS_STRING_TABLE_LOOKUP_DEFINE (fcn_name, \
+	                                      result_type, \
+	                                      const result_type *, \
+	                                      entry_cmd, \
+	                                      unknown_val_cmd, \
+	                                      &, \
+	                                      __VA_ARGS__)
+
+#define NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(fcn_name, \
+                                            result_type, \
+                                            entry_cmd, \
+                                            unknown_val_cmd, \
+                                            ...) \
+	_NM_UTILS_STRING_TABLE_LOOKUP_DEFINE (fcn_name, \
+	                                      result_type, \
+	                                      result_type, \
+	                                      entry_cmd, \
+	                                      unknown_val_cmd, \
+	                                      , \
+	                                      __VA_ARGS__)
+
+/*****************************************************************************/
+
 static inline GTask *
 nm_g_task_new (gpointer source_object,
                GCancellable *cancellable,
@@ -1442,9 +1821,49 @@ guint nm_utils_parse_debug_string (const char *string,
 
 /*****************************************************************************/
 
+static inline gboolean
+nm_utils_strdup_reset (char **dst, const char *src)
+{
+	nm_assert (dst);
+
+	if (nm_streq0 (*dst, src))
+		return FALSE;
+	g_free (*dst);
+	*dst = g_strdup (src);
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+/* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when
+ * the final size is unknown. As such, it has borders for which it allocates
+ * certain buffer sizes.
+ *
+ * The use of these defines is to get favorable allocation sequences.
+ * For example, nm_str_buf_init() asks for an initial allocation size. Note that
+ * it reserves the exactly requested amount, under the assumption that the
+ * user may know how many bytes will be required. However, often the caller
+ * doesn't know in advance, and NMStrBuf grows exponentially by calling
+ * nm_utils_get_next_realloc_size().
+ * Imagine you call nm_str_buf_init() with an initial buffer size 100, and you
+ * add one character at a time. Then the first reallocation will increase the
+ * buffer size only from 100 to 104.
+ * If you however start with an initial buffer size of 104, then the next reallocation
+ * via nm_utils_get_next_realloc_size() gives you 232, and so on. By using
+ * these sizes, it results in one less allocation, if you anyway don't know the
+ * exact size in advance. */
+#define NM_UTILS_GET_NEXT_REALLOC_SIZE_104     ((gsize) 104)
+#define NM_UTILS_GET_NEXT_REALLOC_SIZE_1000    ((gsize) 1000)
+
+gsize nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested);
+
+/*****************************************************************************/
+
 typedef enum {
-	NMU_IFACE_KERNEL = 0,
+	NMU_IFACE_ANY,
+	NMU_IFACE_KERNEL,
 	NMU_IFACE_OVS,
+	NMU_IFACE_OVS_AND_KERNEL,
 } NMUtilsIfaceType;
 
 gboolean nm_utils_ifname_valid_kernel (const char *name, GError **error);