summary refs log tree commit diff
path: root/src/libnm-glib-aux/nm-shared-utils.h
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2024-01-25 09:46:18 +0100
committerMichael Biebl <biebl@debian.org>2024-01-25 09:46:18 +0100
commit70e18d99b8e3e77bb37e218d7ac582130156f8ef (patch)
treed40c587e6d3f0e094ff558e415f1bb9803643214 /src/libnm-glib-aux/nm-shared-utils.h
parentd4d8b2b91f7ba000d97a8b2aab48c85000c11314 (diff)
New upstream version 1.45.90 upstream/1.45.90
Diffstat (limited to 'src/libnm-glib-aux/nm-shared-utils.h')
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.h367
1 files changed, 317 insertions, 50 deletions
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index e37cf5e6..ea38e083 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -97,6 +97,7 @@ typedef enum _nm_packed {
     /* No type, empty value */
     NM_PORT_KIND_NONE,
     NM_PORT_KIND_BOND,
+    NM_PORT_KIND_BRIDGE,
 } NMPortKind;
 
 /*****************************************************************************/
@@ -157,6 +158,7 @@ typedef enum {
 #define _NM_LINK_TYPE_SW_MASTER_FIRST NM_LINK_TYPE_BRIDGE
     NM_LINK_TYPE_BRIDGE,
     NM_LINK_TYPE_BOND,
+    NM_LINK_TYPE_HSR,
     NM_LINK_TYPE_TEAM,
 #define _NM_LINK_TYPE_SW_MASTER_LAST NM_LINK_TYPE_TEAM
 
@@ -331,9 +333,10 @@ gboolean nm_utils_memeqzero(gconstpointer data, gsize length);
 
 extern const void *const _NM_PTRARRAY_EMPTY[1];
 
-#define NM_PTRARRAY_EMPTY(type) ((type const *) _NM_PTRARRAY_EMPTY)
-#define NM_STRV_EMPTY()         ((char **) _NM_PTRARRAY_EMPTY)
-#define NM_STRV_EMPTY_CC()      NM_PTRARRAY_EMPTY(const char *)
+#define NM_PTRARRAY_EMPTY(type)     ((type const *) _NM_PTRARRAY_EMPTY)
+#define NM_STRV_EMPTY()             ((char **) _NM_PTRARRAY_EMPTY)
+#define NM_STRV_EMPTY_CC()          NM_PTRARRAY_EMPTY(const char *)
+#define NM_PTRARRAY_EMPTY_NEW(type) (g_new0(type, 1))
 
 static inline void
 nm_strbuf_init(char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
@@ -528,12 +531,10 @@ gssize _nm_strv_find_first(const char *const *list, gssize len, const char *need
 
 gboolean nm_strv_has_duplicate(const char *const *list, gssize len, gboolean is_sorted);
 
-const char **nm_strv_cleanup_const(const char **strv, gboolean skip_empty, gboolean skip_repeated);
+const char **nm_strv_cleanup_const(const char **strv, gboolean no_empty, gboolean no_duplicates);
 
-char **nm_strv_cleanup(char   **strv,
-                       gboolean strip_whitespace,
-                       gboolean skip_empty,
-                       gboolean skip_repeated);
+char **
+nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean no_empty, gboolean no_duplicates);
 
 gboolean nm_strv_is_same_unordered(const char *const *strv1,
                                    gssize             len1,
@@ -1114,6 +1115,21 @@ nm_utils_error_set_literal(GError **error, int error_code, const char *literal)
 
 /*****************************************************************************/
 
+void nm_gobject_notify_together_by_pspec_v(gpointer                 obj,
+                                           const GParamSpec *const *param_specs,
+                                           gsize                    param_specs_len);
+
+#define nm_gobject_notify_together_by_pspec(obj, ...)                           \
+    G_STMT_START                                                                \
+    {                                                                           \
+        const GParamSpec *const _arr[] = {__VA_ARGS__};                         \
+                                                                                \
+        G_STATIC_ASSERT(NM_NARG(__VA_ARGS__) == G_N_ELEMENTS(_arr));            \
+                                                                                \
+        nm_gobject_notify_together_by_pspec_v((obj), _arr, G_N_ELEMENTS(_arr)); \
+    }                                                                           \
+    G_STMT_END
+
 gboolean nm_g_object_set_property(GObject      *object,
                                   const char   *property_name,
                                   const GValue *value,
@@ -1402,6 +1418,10 @@ nm_g_variant_builder_add_sv_str(GVariantBuilder *builder, const char *key, const
     nm_g_variant_builder_add_sv(builder, key, g_variant_new_string(str));
 }
 
+int nm_g_variant_type_cmp(const GVariantType *type1, const GVariantType *type2);
+
+int nm_g_variant_cmp(GVariant *value1, GVariant *value2);
+
 static inline void
 nm_g_source_destroy_and_unref(GSource *source)
 {
@@ -1851,6 +1871,8 @@ int nm_utils_hashtable_cmp(const GHashTable *a,
                            GCompareDataFunc  cmp_values,
                            gpointer          user_data);
 
+#define nm_strv_empty_new() NM_PTRARRAY_EMPTY_NEW(char *)
+
 char **nm_strv_make_deep_copied(const char **strv);
 
 char **nm_strv_make_deep_copied_n(const char **strv, gsize len);
@@ -1858,13 +1880,18 @@ char **nm_strv_make_deep_copied_n(const char **strv, gsize len);
 static inline char **
 nm_strv_make_deep_copied_nonnull(const char **strv)
 {
-    return nm_strv_make_deep_copied(strv) ?: g_new0(char *, 1);
+    return nm_strv_make_deep_copied(strv) ?: nm_strv_empty_new();
 }
 
-char **_nm_strv_dup(const char *const *strv, gssize len, gboolean deep_copied);
+char **_nm_strv_dup_full(const char *const *strv,
+                         gssize             len,
+                         gboolean           deep_copied,
+                         gboolean           preserve_empty);
 
-#define nm_strv_dup(strv, len, deep_copied) \
-    _nm_strv_dup(NM_CAST_STRV_CC(strv), (len), (deep_copied))
+#define nm_strv_dup_full(strv, len, deep_copied, preserve_empty) \
+    _nm_strv_dup_full(NM_CAST_STRV_CC(strv), (len), (deep_copied), (preserve_empty))
+
+#define nm_strv_dup(strv, len, deep_copied) nm_strv_dup_full((strv), (len), (deep_copied), FALSE)
 
 const char **_nm_strv_dup_packed(const char *const *strv, gssize len);
 
@@ -1942,7 +1969,27 @@ nm_g_array_unref(GArray *arr)
  * When accessing index zero, then this returns NULL if-and-only-if
  * "arr" is NULL or "arr->data" is NULL. In all other cases, this
  * returns the pointer &((Type*) arr->data)[idx]. Note that the pointer
- * may not be followed, if "idx" is equal to "arr->len". */
+ * may not be followed, if "idx" is equal to "arr->len".
+ *
+ * The reason to allow access one element past the length is the
+ * following usage:
+ *
+ *    ptr = nm_g_array_index_p(arr, Type, 0);
+ *    end = nm_g_array_index_p(arr, Type, length);
+ *    for (; ptr < end; ptr++) { ... }
+ *
+ * Another usage is to get a buffer, if the length might be zero. If
+ * length is zero, you cannot dereference the pointer, but it can be convenient
+ * to not require special casing:
+ *
+ *    // length might be zero.
+ *    nm_memdup(nm_g_array_index_p(arr, Type, length), sizeof(Type) * length);
+ *
+ * Note that in C, it's valid point one past the end of an array. So getting
+ * a pointer at index "length" is valid, and what nm_g_array_index_p() allows.
+ * If you don't need that, nm_g_array_index() is usually preferable,
+ * because it asserts against access at index "length".
+ */
 #define nm_g_array_index_p(arr, Type, idx)                                                       \
     ({                                                                                           \
         const GArray *const _arr_55 = (arr);                                                     \
@@ -2509,6 +2556,21 @@ nm_strv_ptrarray_get_unsafe(GPtrArray *arr, guint *out_len)
     return (const char *const *) arr->pdata;
 }
 
+static inline char **
+nm_strv_ptrarray_to_strv_full(const GPtrArray *a, gboolean not_null)
+{
+    if (!a)
+        return not_null ? nm_strv_empty_new() : NULL;
+    return nm_strv_dup_full((const char *const *) a->pdata, a->len, TRUE, TRUE);
+}
+
+static inline char **
+nm_strv_ptrarray_to_strv(const GPtrArray *a)
+{
+    /* Returns never NULL ("not_null"!) */
+    return nm_strv_ptrarray_to_strv_full(a, TRUE);
+}
+
 static inline GPtrArray *
 nm_strv_ptrarray_clone(const GPtrArray *src, gboolean null_if_empty)
 {
@@ -2977,100 +3039,248 @@ nm_strvarray_ensure(GArray **p)
         *p = g_array_new(TRUE, FALSE, sizeof(char *));
         g_array_set_clear_func(*p, nm_indirect_g_free);
     } else
-        nm_assert(g_array_get_element_size(*p) == sizeof(char *));
+        nm_assert(sizeof(char *) == g_array_get_element_size(*p));
 
     return *p;
 }
 
 static inline void
-nm_strvarray_add(GArray *array, const char *str)
+nm_strvarray_add_take(GArray *array, char *str)
 {
-    char *s;
-
     nm_assert(array);
-    nm_assert(g_array_get_element_size(array) == sizeof(char *));
+    nm_assert(sizeof(char *) == g_array_get_element_size(array));
 
-    s = g_strdup(str);
-    g_array_append_val(array, s);
+    /* The array is used as a NULL terminated strv array. Adding NULL is most
+     * likely a bug. Assert against it. */
+    nm_assert(str);
+
+    g_array_append_val(array, str);
+}
+
+static inline void
+nm_strvarray_add(GArray *array, const char *str)
+{
+    nm_strvarray_add_take(array, g_strdup(str));
 }
 
 static inline const char *
-nm_strvarray_get_idx(GArray *array, guint idx)
+nm_strvarray_get_idx(const GArray *array, guint idx)
 {
     return nm_g_array_index(array, const char *, idx);
 }
 
+/* nm_strvarray_get_idxnull_or_greturn() permits access at `len`,
+ * returning NULL. If the access is out of bounds, the assertion
+ * will fail (and also return NULL). */
+#define nm_strvarray_get_idxnull_or_greturn(arr, idx)           \
+    ({                                                          \
+        GArray *_arr = (arr);                                   \
+        gsize   _idx = (idx);                                   \
+        guint   _len = nm_g_array_len(_arr);                    \
+                                                                \
+        g_return_val_if_fail(_idx <= _len, NULL);               \
+                                                                \
+        _idx == _len ? NULL : nm_strvarray_get_idx(_arr, _idx); \
+    })
+
+/**
+ * nm_strvarray_get_strv_full:
+ * @arr: the strvarray.
+ * @length: (out) (nullable): optionally return the length of the result.
+ * @not_null: if true and @arr is NULL, return NM_STRV_EMPTY_CC() (otherwise NULL).
+ * @preserve_empty: if true and the array is empty, return an empty
+ *   strv array. Otherwise, return NULL.
+ *
+ * If "arr" is NULL, this returns NULL, unless "not_null" is true (in which
+ *   case the static NM_STRV_EMPTY_CC() is returned).
+ * If "arr" is empty, it depends on:
+ *   - if "preserve_empty" or "not_null", then the resulting strv array is the empty "arr".
+ *   - otherwise NULL is returned.
+ * Otherwise, returns the non-empty, non-deep-cloned strv array.
+ *
+ * Like nm_strvarray_get_strv_full_dup(), but the strings are not cloned.
+ *
+ * Returns: (transfer none): a strv list or NULL.
+ */
 static inline const char *const *
-nm_strvarray_get_strv_non_empty(GArray *arr, guint *length)
+nm_strvarray_get_strv_full(const GArray *arr,
+                           guint        *length,
+                           gboolean      not_null,
+                           gboolean      preserve_empty)
 {
-    nm_assert(!arr || g_array_get_element_size(arr) == sizeof(char *));
-
-    if (!arr || arr->len == 0) {
+    if (!arr) {
         NM_SET_OUT(length, 0);
-        return NULL;
+        return not_null ? NM_STRV_EMPTY_CC() : NULL;
     }
 
+    nm_assert(sizeof(char *) == g_array_get_element_size((GArray *) arr));
+
     NM_SET_OUT(length, arr->len);
+
+    if (arr->len == 0 && !(preserve_empty || not_null))
+        return NULL;
+
     return &g_array_index(arr, const char *, 0);
 }
 
+/**
+ * nm_strvarray_get_strv_full_dup:
+ * @arr: the strvarray.
+ * @length: (out) (nullable): optionally return the length of the result.
+ * @not_null: if true, never return NULL but allocate an empty strv array.
+ * @preserve_empty: if true and the array is empty, return an empty
+ *   strv array. Otherwise, return NULL.
+ *
+ * If "arr" is NULL, this returns NULL, unless "not_null" is true (in which case
+ *   am empty strv array is allocated.
+ * If "arr" is empty, it depends on:
+ *   - if "preserve_empty" || "not_null", then the resulting strv array is allocated (and empty).
+ *   - otherwise, NULL is returned.
+ * Otherwise, return the non-empty, deep-cloned strv array.
+ *
+ * Like nm_strvarray_get_strv_full(), but the strings are cloned.
+ *
+ * Returns: (transfer full): a deep-cloned strv list or NULL.
+ */
 static inline char **
-nm_strvarray_get_strv_non_empty_dup(GArray *arr, guint *length)
+nm_strvarray_get_strv_full_dup(const GArray *arr,
+                               guint        *length,
+                               gboolean      not_null,
+                               gboolean      preserve_empty)
 {
-    const char *const *strv;
+    if (!arr) {
+        NM_SET_OUT(length, 0);
+        return not_null ? nm_strv_empty_new() : NULL;
+    }
 
-    nm_assert(!arr || g_array_get_element_size(arr) == sizeof(char *));
+    nm_assert(sizeof(char *) == g_array_get_element_size((GArray *) arr));
 
-    if (!arr || arr->len == 0) {
-        NM_SET_OUT(length, 0);
+    NM_SET_OUT(length, arr->len);
+
+    if (arr->len == 0) {
+        if (preserve_empty || not_null)
+            return nm_strv_empty_new();
         return NULL;
     }
 
-    NM_SET_OUT(length, arr->len);
-    strv = &g_array_index(arr, const char *, 0);
-    return nm_strv_dup(strv, arr->len, TRUE);
+    return nm_strv_dup(&g_array_index(arr, const char *, 0), arr->len, TRUE);
 }
 
+/**
+ * nm_strvarray_get_strv_notnull:
+ * @arr: the strvarray.
+ * @length: (out) (nullable): optionally return the length of the result.
+ *
+ * This never returns NULL. If @arr is NULL, this returns NM_STRV_EMPTY_CC().
+ *
+ * Like nm_strvarray_get_strv_notempty(), but never returns NULL.
+ *
+ * Returns: (transfer none): a pointer to the strv list in @arr or NM_STRV_EMPTY_CC().
+ */
 static inline const char *const *
-nm_strvarray_get_strv(GArray **arr, guint *length)
+nm_strvarray_get_strv_notnull(const GArray *arr, guint *length)
 {
-    if (!*arr) {
-        NM_SET_OUT(length, 0);
-        return (const char *const *) arr;
-    }
+    return nm_strvarray_get_strv_full(arr, length, TRUE, TRUE);
+}
 
-    nm_assert(g_array_get_element_size(*arr) == sizeof(char *));
+/**
+ * nm_strvarray_get_strv_notempty:
+ * @arr: the strvarray.
+ * @length: (out) (nullable): optionally return the length of the result.
+ *
+ * This never returns an empty strv array. If @arr is NULL or empty, this
+ * returns NULL.
+ *
+ * Like nm_strvarray_get_strv_notempty_dup(), but does not clone strings.
+ *
+ * Returns: (transfer none): a pointer to the strv list in @arr or NULL.
+ */
+static inline const char *const *
+nm_strvarray_get_strv_notempty(const GArray *arr, guint *length)
+{
+    return nm_strvarray_get_strv_full(arr, length, FALSE, FALSE);
+}
 
-    NM_SET_OUT(length, (*arr)->len);
-    return &g_array_index(*arr, const char *, 0);
+/**
+ * nm_strvarray_get_strv_notempty_dup:
+ * @arr: the strvarray.
+ * @length: (out) (nullable): optionally return the length of the result.
+ *
+ * This never returns an empty strv array. If @arr is NULL or empty, this
+ * returns NULL.
+ *
+ * Like nm_strvarray_get_strv_notempty(), but clones strings.
+ *
+ * Returns: (transfer full): a deep-cloned strv list or NULL.
+ */
+static inline char **
+nm_strvarray_get_strv_notempty_dup(const GArray *arr, guint *length)
+{
+    return nm_strvarray_get_strv_full_dup(arr, length, FALSE, FALSE);
 }
 
+/**
+ * nm_strvarray_set_strv_full:
+ * @array: a pointer to the array to set.
+ * @strv: the strv array. May be NULL.
+ * @preserve_empty: how to treat if strv is empty (strv[0]==NULL).
+ *
+ * The old array will be freed (in a way so that the function is self-assignment
+ * safe).
+ *
+ * If "strv" is NULL, then the resulting GArray is NULL.
+ * If "strv" is empty, then it depends on "preserve_empty":
+ *   - if "preserve_empty", then the resulting GArray is allocated (and empty).
+ *   - if "!preserve_empty", then the resulting GArray is NULL.
+ * If "strv" is not empty, a GArray gets allocated and the strv array deep-cloned.
+ */
 static inline void
-nm_strvarray_set_strv(GArray **array, const char *const *strv)
+nm_strvarray_set_strv_full(GArray **array, const char *const *strv, gboolean preserve_empty)
 {
     gs_unref_array GArray *array_old = NULL;
 
     array_old = g_steal_pointer(array);
 
-    nm_assert(!array_old || g_array_get_element_size(array_old) == sizeof(char *));
+    nm_assert(!array_old || sizeof(char *) == g_array_get_element_size(array_old));
+
+    if (!strv)
+        return;
 
-    if (!strv || !strv[0])
+    if (!strv[0] && !preserve_empty) {
+        /* An empty strv array is treated like NULL. Don't allocate a GArray. */
         return;
+    }
 
     nm_strvarray_ensure(array);
     for (; strv[0]; strv++)
         nm_strvarray_add(*array, strv[0]);
 }
 
+/**
+ * nm_strvarray_set_strv:
+ * @array: a pointer to the array to set.
+ * @strv: the strv array. May be NULL.
+ *
+ * The old array will be freed (in a way so that the function is self-assignment
+ * safe).
+ *
+ * Note that this will never initialize an empty GArray. If strv is NULL or
+ * empty, the @array pointer will be set to NULL. */
+static inline void
+nm_strvarray_set_strv(GArray **array, const char *const *strv)
+{
+    nm_strvarray_set_strv_full(array, strv, FALSE);
+}
+
 static inline gssize
-nm_strvarray_find_first(GArray *strv, const char *needle)
+nm_strvarray_find_first(const GArray *strv, const char *needle)
 {
     guint i;
 
     nm_assert(needle);
 
     if (strv) {
-        nm_assert(g_array_get_element_size(strv) == sizeof(char *));
+        nm_assert(sizeof(char *) == g_array_get_element_size((GArray *) strv));
         for (i = 0; i < strv->len; i++) {
             if (nm_streq(needle, g_array_index(strv, const char *, i)))
                 return i;
@@ -3079,6 +3289,8 @@ nm_strvarray_find_first(GArray *strv, const char *needle)
     return -1;
 }
 
+#define nm_strvarray_contains(strv, needle) (nm_strvarray_find_first((strv), (needle)) >= 0)
+
 static inline gboolean
 nm_strvarray_remove_first(GArray *strv, const char *needle)
 {
@@ -3093,11 +3305,42 @@ nm_strvarray_remove_first(GArray *strv, const char *needle)
     return TRUE;
 }
 
+#define nm_strvarray_remove_index(strv, idx)                          \
+    G_STMT_START                                                      \
+    {                                                                 \
+        GArray *const _strv = (strv);                                 \
+        typeof(idx)   _idx  = (idx);                                  \
+                                                                      \
+        nm_assert(_strv);                                             \
+        nm_assert((uintmax_t) _idx < _strv->len);                     \
+        nm_assert(sizeof(char *) == g_array_get_element_size(_strv)); \
+                                                                      \
+        g_array_remove_index(_strv, (guint) _idx);                    \
+    }                                                                 \
+    G_STMT_END
+
+static inline void
+nm_strvarray_ensure_and_add(GArray **p, const char *str)
+{
+    nm_strvarray_add(nm_strvarray_ensure(p), str);
+}
+
+static inline gboolean
+nm_strvarray_ensure_and_add_unique(GArray **p, const char *str)
+{
+    nm_assert(p);
+
+    if (nm_strvarray_contains(*p, str))
+        return FALSE;
+    nm_strvarray_add(nm_strvarray_ensure(p), str);
+    return TRUE;
+}
+
 static inline int
 nm_strvarray_cmp(const GArray *a, const GArray *b)
 {
-    nm_assert(!a || sizeof(const char *const *) == g_array_get_element_size((GArray *) a));
-    nm_assert(!b || sizeof(const char *const *) == g_array_get_element_size((GArray *) b));
+    nm_assert(!a || sizeof(char *) == g_array_get_element_size((GArray *) a));
+    nm_assert(!b || sizeof(char *) == g_array_get_element_size((GArray *) b));
 
     NM_CMP_SELF(a, b);
 
@@ -3109,7 +3352,7 @@ nm_strvarray_cmp(const GArray *a, const GArray *b)
 static inline int
 _nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len)
 {
-    nm_assert(!strv || sizeof(const char *const *) == g_array_get_element_size((GArray *) strv));
+    nm_assert(!strv || sizeof(char *) == g_array_get_element_size((GArray *) strv));
 
     return nm_strv_cmp_n(nm_g_array_data(strv), strv ? ((gssize) strv->len) : -1, ss, ss_len);
 }
@@ -3119,6 +3362,24 @@ _nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len)
 #define nm_strvarray_equal_strv(strv, ss, ss_len) \
     (nm_strvarray_cmp_strv((strv), (ss), (ss_len)) == 0)
 
+static inline gboolean
+nm_strvarray_clear(GArray **array)
+{
+    gboolean cleared = FALSE;
+
+    nm_assert(array);
+    nm_assert(!*array || sizeof(char *) == g_array_get_element_size(*array));
+
+    if (*array) {
+        /* We always clear the GArray, but we return TRUE only if the
+         * array was non-empty before. */
+        if ((*array)->len > 0)
+            cleared = TRUE;
+        nm_clear_pointer(array, g_array_unref);
+    }
+    return cleared;
+}
+
 /*****************************************************************************/
 
 struct _NMVariantAttributeSpec {
@@ -3163,6 +3424,12 @@ gboolean nm_utils_is_localhost(const char *name);
 
 gboolean nm_utils_is_specific_hostname(const char *name);
 
+struct passwd;
+
+struct passwd *nm_getpwuid(uid_t uid);
+
+const char *nm_passwd_name(const struct passwd *pw);
+
 char    *nm_utils_uid_to_name(uid_t uid);
 gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid);