diff options
Diffstat (limited to 'shared/nm-glib-aux')
| -rw-r--r-- | shared/nm-glib-aux/nm-glib.h | 12 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-macros-internal.h | 46 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.c | 190 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.h | 181 |
4 files changed, 406 insertions, 23 deletions
diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h index 7a5b8edd..2df51795 100644 --- a/shared/nm-glib-aux/nm-glib.h +++ b/shared/nm-glib-aux/nm-glib.h @@ -382,9 +382,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table, /*****************************************************************************/ -#if !GLIB_CHECK_VERSION(2, 44, 0) static inline gpointer -g_steal_pointer (gpointer pp) +_nm_g_steal_pointer (gpointer pp) { gpointer *ptr = (gpointer *) pp; gpointer ref; @@ -394,13 +393,20 @@ g_steal_pointer (gpointer pp) return ref; } + +#if !GLIB_CHECK_VERSION(2, 44, 0) +static inline gpointer +g_steal_pointer (gpointer pp) +{ + return _nm_g_steal_pointer (pp); +} #endif #ifdef g_steal_pointer #undef g_steal_pointer #endif #define g_steal_pointer(pp) \ - ((typeof (*(pp))) g_steal_pointer (pp)) + ((typeof (*(pp))) _nm_g_steal_pointer (pp)) /*****************************************************************************/ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index d5a8513e..f56ed856 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -630,6 +630,14 @@ NM_G_ERROR_MSG (GError *error) NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ }) +#define NM_ENSURE_NOT_NULL(ptr) \ + ({ \ + typeof (ptr) _ptr = (ptr); \ + \ + nm_assert (_ptr != NULL); \ + _ptr; \ + }) + #if _NM_CC_SUPPORT_GENERIC /* returns @value, if the type of @value matches @type. * This requires support for C11 _Generic(). If no support is @@ -929,23 +937,38 @@ nm_streq0 (const char *s1, const char *s2) #define NM_STR_HAS_PREFIX(str, prefix) \ ({ \ - const char *const _str = (str); \ + const char *const _str_has_prefix = (str); \ \ - _str && (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0); \ + nm_assert (strlen (prefix) == NM_STRLEN (prefix)); \ + \ + _str_has_prefix \ + && (strncmp (_str_has_prefix, ""prefix"", NM_STRLEN (prefix)) == 0); \ }) #define NM_STR_HAS_SUFFIX(str, suffix) \ ({ \ - const char *_str; \ + const char *const _str_has_suffix = (str); \ gsize _l; \ \ - ( (_str = (str)) \ - && ((_l = strlen (_str)) >= NM_STRLEN (suffix)) \ - && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ + nm_assert (strlen (suffix) == NM_STRLEN (suffix)); \ + \ + ( _str_has_suffix \ + && ((_l = strlen (_str_has_suffix)) >= NM_STRLEN (suffix)) \ + && (memcmp (&_str_has_suffix[_l - NM_STRLEN (suffix)], \ ""suffix"", \ NM_STRLEN (suffix)) == 0)); \ }) +/* whether @str starts with the string literal @prefix and is followed by + * some other text. It is like NM_STR_HAS_PREFIX() && !nm_streq() together. */ +#define NM_STR_HAS_PREFIX_WITH_MORE(str, prefix) \ + ({ \ + const char *const _str_has_prefix_with_more = (str); \ + \ + NM_STR_HAS_PREFIX (_str_has_prefix_with_more, ""prefix"") \ + && _str_has_prefix_with_more[NM_STRLEN (prefix)] != '\0'; \ + }) + /*****************************************************************************/ static inline GString * @@ -966,16 +989,23 @@ nm_gstring_add_space_delimiter (GString *str) return str; } +static inline gboolean +nm_str_is_empty (const char *str) +{ + /* %NULL is also accepted, and also "empty". */ + return !str || !str[0]; +} + static inline const char * nm_str_not_empty (const char *str) { - return str && str[0] ? str : NULL; + return !nm_str_is_empty (str) ? str : NULL; } static inline char * nm_strdup_not_empty (const char *str) { - return str && str[0] ? g_strdup (str) : NULL; + return !nm_str_is_empty (str) ? g_strdup (str) : NULL; } static inline char * diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 5dd099a7..39e98a31 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -13,10 +13,14 @@ #include <sys/syscall.h> #include <glib-unix.h> #include <net/if.h> +#include <net/ethernet.h> #include "nm-errno.h" #include "nm-str-buf.h" +G_STATIC_ASSERT (sizeof (NMUtilsNamedEntry) == sizeof (const char *)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMUtilsNamedValue, value_ptr) == sizeof (const char *)); + /*****************************************************************************/ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; @@ -85,6 +89,11 @@ nm_ip_addr_set_from_untrusted (int addr_family, /*****************************************************************************/ +G_STATIC_ASSERT (ETH_ALEN == sizeof (struct ether_addr)); +G_STATIC_ASSERT (ETH_ALEN == 6); + +/*****************************************************************************/ + gsize nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested) { @@ -2456,11 +2465,12 @@ _str_buf_append_c_escape_octal (NMStrBuf *strbuf, } gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) +nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free) { + gboolean strip_spaces = NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES); NMStrBuf strbuf; - gsize len; const char *s; + gsize len; g_return_val_if_fail (to_free, NULL); g_return_val_if_fail (out_len, NULL); @@ -2471,16 +2481,29 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr return NULL; } + if (strip_spaces) + str = nm_str_skip_leading_spaces (str); + len = strlen (str); s = memchr (str, '\\', len); if (!s) { + if ( strip_spaces + && len > 0 + && g_ascii_isspace (str[len - 1])) { + len--; + while ( len > 0 + && g_ascii_isspace (str[len - 1])) + len--; + *out_len = len; + return (*to_free = g_strndup (str, len)); + } *out_len = len; *to_free = NULL; return str; } - nm_str_buf_init (&strbuf, len, FALSE); + nm_str_buf_init (&strbuf, len + 1u, FALSE); nm_str_buf_append_len (&strbuf, str, s - str); str = s; @@ -2494,7 +2517,7 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr ch = (++str)[0]; if (ch == '\0') { - // error. Trailing '\\' + /* error. Trailing '\\' */ break; } @@ -2533,7 +2556,14 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr s = strchr (str, '\\'); if (!s) { - nm_str_buf_append (&strbuf, str); + gsize l = strlen (str); + + if (strip_spaces) { + while ( l > 0 + && g_ascii_isspace (str[l - 1])) + l--; + } + nm_str_buf_append_len (&strbuf, str, l); break; } @@ -2541,6 +2571,11 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr str = s; } + /* assert that no reallocation was necessary. For one, unescaping should + * never result in a longer string than the input. Also, when unescaping + * secrets, we want to ensure that we don't leak secrets in memory. */ + nm_assert (strbuf.allocated == len + 1u); + return (*to_free = nm_str_buf_finalize (&strbuf, out_len)); } @@ -2670,16 +2705,33 @@ nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); } +char * +nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags) +{ + const char *s_const; + char *s; + + s_const = nm_utils_buf_utf8safe_escape (buf, buflen, flags, &s); + nm_assert (!s || s == s_const); + return s ?: g_strdup (s_const); +} + /*****************************************************************************/ const char * -nm_utils_str_utf8safe_unescape (const char *str, char **to_free) +nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { + const char *res; gsize len; g_return_val_if_fail (to_free, NULL); - return nm_utils_buf_utf8safe_unescape (str, &len, (gpointer *) to_free); + res = nm_utils_buf_utf8safe_unescape (str, flags, &len, (gpointer *) to_free); + + nm_assert ( (!res && len == 0) + || (strlen (res) <= len)); + + return res; } /** @@ -2737,11 +2789,11 @@ nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) } char * -nm_utils_str_utf8safe_unescape_cp (const char *str) +nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) { char *s; - str = nm_utils_str_utf8safe_unescape (str, &s); + str = nm_utils_str_utf8safe_unescape (str, flags, &s); return s ?: g_strdup (str); } @@ -4817,3 +4869,123 @@ nm_str_buf_append_printf (NMStrBuf *strbuf, strbuf->_priv_len += (gsize) l; } + +/*****************************************************************************/ + +/** + * nm_indirect_g_free: + * @arg: a pointer to a pointer that is to be freed. + * + * This does the same as nm_clear_g_free(arg) (g_clear_pointer (arg, g_free)). + * This is for example useful when you have a GArray with pointers and a + * clear function to free them. g_array_set_clear_func()'s destroy notify + * function gets a pointer to the array location, so we have to follow + * the first pointer. + */ +void +nm_indirect_g_free (gpointer arg) +{ + gpointer *p = arg; + + nm_clear_g_free (p); +} + +/*****************************************************************************/ + +static char * +attribute_escape (const char *src, char c1, char c2) +{ + char *ret, *dest; + + dest = ret = g_malloc (strlen (src) * 2 + 1); + + while (*src) { + if (*src == c1 || *src == c2 || *src == '\\') + *dest++ = '\\'; + *dest++ = *src++; + } + *dest++ = '\0'; + + return ret; +} + +void +_nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator) +{ + const char *name, *value; + GVariant *variant; + char *escaped; + char buf[64]; + char sep = 0; + guint i; + + for (i = 0; i < num_values; i++) { + name = values[i].name; + variant = (GVariant *) values[i].value_ptr; + value = NULL; + + if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) + value = nm_sprintf_buf (buf, "%u", g_variant_get_uint32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)) + value = nm_sprintf_buf (buf, "%d", (int) g_variant_get_int32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64)) + value = nm_sprintf_buf (buf, "%"G_GUINT64_FORMAT, g_variant_get_uint64 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) + value = nm_sprintf_buf (buf, "%hhu", g_variant_get_byte (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) + value = g_variant_get_boolean (variant) ? "true" : "false"; + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) + value = g_variant_get_string (variant, NULL); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING)) { + /* FIXME: there is no guarantee that the byte array + * is valid UTF-8.*/ + value = g_variant_get_bytestring (variant); + } else + continue; + + if (sep) + g_string_append_c (str, sep); + + escaped = attribute_escape (name, attr_separator, key_value_separator); + g_string_append (str, escaped); + g_free (escaped); + + g_string_append_c (str, key_value_separator); + + escaped = attribute_escape (value, attr_separator, key_value_separator); + g_string_append (str, escaped); + g_free (escaped); + + sep = attr_separator; + } +} + +char * +_nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator) +{ + GString *str = NULL; + gs_free NMUtilsNamedValue *values = NULL; + guint len; + + g_return_val_if_fail (attr_separator, NULL); + g_return_val_if_fail (key_value_separator, NULL); + + if (!attributes || !g_hash_table_size (attributes)) + return NULL; + + values = nm_utils_named_values_from_str_dict (attributes, &len); + + str = g_string_new (""); + _nm_utils_format_variant_attributes_full (str, + values, + len, + attr_separator, + key_value_separator); + return g_string_free (str, FALSE); +} diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index d5990c2d..eeb8be5b 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -178,6 +178,24 @@ nm_ip4_addr_is_localhost (in_addr_t addr4) /*****************************************************************************/ +struct ether_addr; + +static inline int +nm_utils_ether_addr_cmp (const struct ether_addr *a1, const struct ether_addr *a2) +{ + nm_assert (a1); + nm_assert (a2); + return memcmp (a1, a2, 6 /*ETH_ALEN*/); +} + +static inline gboolean +nm_utils_ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2) +{ + return nm_utils_ether_addr_cmp (a1, a2) == 0; +} + +/*****************************************************************************/ + #define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN static inline const char * @@ -926,6 +944,21 @@ _nm_g_slice_free_fcn_define (32) /*****************************************************************************/ +/* Like g_error_matches() however: + * - as macro it is always inlined. + * - the @domain is usually a error quark getter function that cannot + * be inlined. This macro calls the getter only if there is an error (lazy). + * - accept a list of allowed codes, instead of only one. + */ +#define nm_g_error_matches(error, err_domain, ...) \ + ({ \ + const GError *const _error = (error); \ + \ + _error \ + && _error->domain == (err_domain) \ + && NM_IN_SET (_error->code, __VA_ARGS__); \ + }) + static inline void nm_g_set_error_take (GError **error, GError *error_take) { @@ -1143,6 +1176,25 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +#define _NM_G_PARAM_SPEC_CAST(param_spec, _value_type, _c_type) \ + ({ \ + const GParamSpec *const _param_spec = (param_spec); \ + \ + nm_assert ( !_param_spec \ + || _param_spec->value_type == (_value_type)); \ + ((const _c_type *) _param_spec); \ + }) + +#define NM_G_PARAM_SPEC_CAST_BOOLEAN(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_BOOLEAN, GParamSpecBoolean) +#define NM_G_PARAM_SPEC_CAST_UINT(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT, GParamSpecUInt) +#define NM_G_PARAM_SPEC_CAST_UINT64(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT64, GParamSpecUInt64) + +#define NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(param_spec) (NM_G_PARAM_SPEC_CAST_BOOLEAN (NM_ENSURE_NOT_NULL (param_spec))->default_value) +#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT(param_spec) (NM_G_PARAM_SPEC_CAST_UINT (NM_ENSURE_NOT_NULL (param_spec))->default_value) +#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(param_spec) (NM_G_PARAM_SPEC_CAST_UINT64 (NM_ENSURE_NOT_NULL (param_spec))->default_value) + +/*****************************************************************************/ + GType nm_g_type_find_implementing_class_for_property (GType gtype, const char *pname); @@ -1150,20 +1202,39 @@ GType nm_g_type_find_implementing_class_for_property (GType gtype, typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, + + /* This flag only has an effect during escaping. */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, + + /* This flag only has an effect during escaping. */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, + + /* This flag only has an effect during escaping to ensure we + * don't leak secrets in memory. Note that during unescape we + * know the maximum result size from the beginning, and no + * reallocation happens. Thus, unescape always avoids leaking + * secrets already. */ NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET = 0x0004, + + /* This flag only has an effect during unescaping. It means + * that non-escaped whitespaces (g_ascii_isspace()) will be + * stripped from the front and end of the string. Note that + * this flag is only useful for gracefully accepting user input + * with spaces. With this flag, escape and unescape may no longer + * yield the original input. */ + NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES = 0x0008, } NMUtilsStrUtf8SafeFlags; const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free); +char *nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags); 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); +gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, 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); +const char *nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, 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_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags); @@ -1211,6 +1282,30 @@ nm_g_variant_is_of_type (GVariant *value, } static inline void +nm_g_variant_builder_add_sv (GVariantBuilder *builder, const char *key, GVariant *val) +{ + g_variant_builder_add (builder, "{sv}", key, val); +} + +static inline void +nm_g_variant_builder_add_sv_bytearray (GVariantBuilder *builder, const char *key, const guint8 *arr, gsize len) +{ + g_variant_builder_add (builder, "{sv}", key, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, arr, len, 1)); +} + +static inline void +nm_g_variant_builder_add_sv_uint32 (GVariantBuilder *builder, const char *key, guint32 val) +{ + nm_g_variant_builder_add_sv (builder, key, g_variant_new_uint32 (val)); +} + +static inline void +nm_g_variant_builder_add_sv_str (GVariantBuilder *builder, const char *key, const char *str) +{ + nm_g_variant_builder_add_sv (builder, key, g_variant_new_string (str)); +} + +static inline void nm_g_source_destroy_and_unref (GSource *source) { g_source_destroy (source); @@ -1355,6 +1450,8 @@ typedef struct { }; } NMUtilsNamedValue; +#define NM_UTILS_NAMED_VALUE_INIT(n, v) { .name = (n), .value_ptr = (v) } + NMUtilsNamedValue *nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash, guint *out_len, GCompareDataFunc compare_func, @@ -1436,6 +1533,22 @@ char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator); /*****************************************************************************/ static inline guint +nm_g_array_len (const GArray *arr) +{ + return arr ? arr->len : 0u; +} + +/*****************************************************************************/ + +static inline guint +nm_g_ptr_array_len (const GPtrArray *arr) +{ + return arr ? arr->len : 0u; +} + +/*****************************************************************************/ + +static inline guint nm_g_hash_table_size (GHashTable *hash) { return hash ? g_hash_table_size (hash) : 0u; @@ -1848,6 +1961,8 @@ nm_utils_strdup_reset (char **dst, const char *src) return TRUE; } +void nm_indirect_g_free (gpointer arg); + /*****************************************************************************/ /* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when @@ -1887,4 +2002,64 @@ gboolean nm_utils_ifname_valid (const char* name, NMUtilsIfaceType type, GError **error); +/*****************************************************************************/ + +static inline GArray * +nm_strvarray_ensure (GArray **p) +{ + if (!*p) { + *p = g_array_new (TRUE, FALSE, sizeof (char *)); + g_array_set_clear_func (*p, nm_indirect_g_free); + } + return *p; +} + +static inline void +nm_strvarray_add (GArray *array, const char *str) +{ + char *s; + + s = g_strdup (str); + g_array_append_val (array, s); +} + +static inline const char *const* +nm_strvarray_get_strv (GArray **arr, guint *length) +{ + if (!*arr) { + NM_SET_OUT (length, 0); + return (const char *const*) arr; + } + + NM_SET_OUT (length, (*arr)->len); + return &g_array_index (*arr, const char *, 0); +} + +static inline void +nm_strvarray_set_strv (GArray **array, const char *const*strv) +{ + gs_unref_array GArray *array_old = NULL; + + array_old = g_steal_pointer (array); + + if (!strv || !strv[0]) + return; + + nm_strvarray_ensure (array); + for (; strv[0]; strv++) + nm_strvarray_add (*array, strv[0]); +} + +/*****************************************************************************/ + +void _nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator); + +char *_nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator); + #endif /* __NM_SHARED_UTILS_H__ */ |