diff options
Diffstat (limited to 'shared/nm-glib-aux')
29 files changed, 2027 insertions, 97 deletions
diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h index 5c73f574..7512730d 100644 --- a/shared/nm-glib-aux/nm-c-list.h +++ b/shared/nm-glib-aux/nm-c-list.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -33,6 +32,8 @@ _what && c_list_contains (list, &_what->member); \ }) +/*****************************************************************************/ + typedef struct { CList lst; void *data; @@ -48,21 +49,34 @@ nm_c_list_elem_new_stale (void *data) return elem; } -static inline void * -nm_c_list_elem_get (CList *lst) +static inline gboolean +nm_c_list_elem_free_full (NMCListElem *elem, GDestroyNotify free_fcn) { - if (!lst) - return NULL; - return c_list_entry (lst, NMCListElem, lst)->data; + if (!elem) + return FALSE; + c_list_unlink_stale (&elem->lst); + if (free_fcn) + free_fcn (elem->data); + g_slice_free (NMCListElem, elem); + return TRUE; } -static inline void +static inline gboolean nm_c_list_elem_free (NMCListElem *elem) { - if (elem) { - c_list_unlink_stale (&elem->lst); - g_slice_free (NMCListElem, elem); - } + return nm_c_list_elem_free_full (elem, NULL); +} + +static inline void * +nm_c_list_elem_free_steal (NMCListElem *elem) +{ + gpointer data; + + if (!elem) + return NULL; + data = elem->data; + nm_c_list_elem_free_full (elem, NULL); + return data; } static inline void @@ -70,22 +84,53 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn) { NMCListElem *elem; - while ((elem = c_list_first_entry (head, NMCListElem, lst))) { - if (free_fcn) - free_fcn (elem->data); - c_list_unlink_stale (&elem->lst); - g_slice_free (NMCListElem, elem); + while ((elem = c_list_first_entry (head, NMCListElem, lst))) + nm_c_list_elem_free_full (elem, free_fcn); +} + +/** + * nm_c_list_elem_find_first: + * @head: the @CList head of a list containing #NMCListElem elements. + * Note that the head is not itself part of the list. + * @needle: the needle pointer. + * + * Iterates the list and returns the first #NMCListElem with the matching @needle, + * using pointer equality. + * + * Returns: the found list element or %NULL if not found. + */ +static inline NMCListElem * +nm_c_list_elem_find_first (CList *head, gconstpointer needle) +{ + NMCListElem *elem; + + c_list_for_each_entry (elem, head, lst) { + if (elem->data == needle) + return elem; } + return NULL; } /*****************************************************************************/ +/** + * nm_c_list_move_before: + * @lst: the list element to which @elem will be prepended. + * @elem: the list element to move. + * + * This unlinks @elem from the current list and linkes it before + * @lst. This is like c_list_link_before(), except that @elem must + * be initialized and linked. Note that @elem may be linked in @lst + * or in another list. In both cases it gets moved. + * + * Returns: %TRUE if there were any changes. %FALSE if elem was already + * linked at the right place. + */ static inline gboolean nm_c_list_move_before (CList *lst, CList *elem) { nm_assert (lst); nm_assert (elem); - nm_assert (c_list_contains (lst, elem)); if ( lst != elem && lst->prev != elem) { @@ -97,12 +142,24 @@ nm_c_list_move_before (CList *lst, CList *elem) } #define nm_c_list_move_tail(lst, elem) nm_c_list_move_before (lst, elem) +/** + * nm_c_list_move_after: + * @lst: the list element to which @elem will be prepended. + * @elem: the list element to move. + * + * This unlinks @elem from the current list and linkes it after + * @lst. This is like c_list_link_after(), except that @elem must + * be initialized and linked. Note that @elem may be linked in @lst + * or in another list. In both cases it gets moved. + * + * Returns: %TRUE if there were any changes. %FALSE if elem was already + * linked at the right place. + */ static inline gboolean nm_c_list_move_after (CList *lst, CList *elem) { nm_assert (lst); nm_assert (elem); - nm_assert (c_list_contains (lst, elem)); if ( lst != elem && lst->next != elem) { @@ -114,4 +171,14 @@ nm_c_list_move_after (CList *lst, CList *elem) } #define nm_c_list_move_front(lst, elem) nm_c_list_move_after (lst, elem) +#define nm_c_list_free_all(lst, type, member, destroy_fcn) \ + G_STMT_START { \ + CList *const _lst = (lst); \ + type *_elem; \ + \ + while ((_elem = c_list_first_entry (_lst, type, member))) { \ + destroy_fcn (_elem); \ + } \ + } G_STMT_END + #endif /* __NM_C_LIST_H__ */ diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c new file mode 100644 index 00000000..083c4fee --- /dev/null +++ b/shared/nm-glib-aux/nm-dbus-aux.c @@ -0,0 +1,69 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dbus-aux.h" + +/*****************************************************************************/ + +static void +_nm_dbus_connection_call_get_name_owner_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + const char *owner = NULL; + gpointer orig_user_data; + NMDBusConnectionCallGetNameOwnerCb callback; + + nm_utils_user_data_unpack (user_data, &orig_user_data, &callback); + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); + if (ret) + g_variant_get (ret, "(&s)", &owner); + + callback (owner, error, orig_user_data); +} + +void +nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection, + const char *service_name, + int timeout_msec, + GCancellable *cancellable, + NMDBusConnectionCallGetNameOwnerCb callback, + gpointer user_data) +{ + nm_assert (callback); + + g_dbus_connection_call (dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new ("(s)", service_name), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + timeout_msec, + cancellable, + _nm_dbus_connection_call_get_name_owner_cb, + nm_utils_user_data_pack (user_data, callback)); +} diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h new file mode 100644 index 00000000..271a7d9c --- /dev/null +++ b/shared/nm-glib-aux/nm-dbus-aux.h @@ -0,0 +1,102 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_DBUS_AUX_H__ +#define __NM_DBUS_AUX_H__ + +#include "nm-std-aux/nm-dbus-compat.h" + +/*****************************************************************************/ + +static inline gboolean +nm_clear_g_dbus_connection_signal (GDBusConnection *dbus_connection, + guint *id) +{ + guint v; + + if ( id + && (v = *id)) { + *id = 0; + g_dbus_connection_signal_unsubscribe (dbus_connection, v); + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ + +static inline void +nm_dbus_connection_call_start_service_by_name (GDBusConnection *dbus_connection, + const char *name, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_connection_call (dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "StartServiceByName", + g_variant_new ("(su)", name, 0u), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + timeout_msec, + cancellable, + callback, + user_data); +} + +/*****************************************************************************/ + +static inline guint +nm_dbus_connection_signal_subscribe_name_owner_changed (GDBusConnection *dbus_connection, + const char *service_name, + GDBusSignalCallback callback, + gpointer user_data, + GDestroyNotify user_data_free_func) + +{ + return g_dbus_connection_signal_subscribe (dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged", + DBUS_PATH_DBUS, + service_name, + G_DBUS_SIGNAL_FLAGS_NONE, + callback, + user_data, + user_data_free_func); +} + +typedef void (*NMDBusConnectionCallGetNameOwnerCb) (const char *name_owner, + GError *error, + gpointer user_data); + +void nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection, + const char *service_name, + int timeout_msec, + GCancellable *cancellable, + NMDBusConnectionCallGetNameOwnerCb callback, + gpointer user_data); + +/*****************************************************************************/ + +#endif /* __NM_DBUS_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c index 5bdc3e3c..345062ca 100644 --- a/shared/nm-glib-aux/nm-dedup-multi.c +++ b/shared/nm-glib-aux/nm-dedup-multi.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h index 82c6f1e9..ca15c516 100644 --- a/shared/nm-glib-aux/nm-dedup-multi.h +++ b/shared/nm-glib-aux/nm-dedup-multi.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c index a4f6e809..b16267a5 100644 --- a/shared/nm-glib-aux/nm-enum-utils.c +++ b/shared/nm-glib-aux/nm-enum-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h index 1827fdf4..20db07cc 100644 --- a/shared/nm-glib-aux/nm-enum-utils.h +++ b/shared/nm-glib-aux/nm-enum-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h index e941e067..bdb7ea5b 100644 --- a/shared/nm-glib-aux/nm-glib.h +++ b/shared/nm-glib-aux/nm-glib.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -523,10 +522,18 @@ _nm_g_variant_new_printf (const char *format_string, ...) /*****************************************************************************/ -#if !GLIB_CHECK_VERSION (2, 56, 0) +/* Recent glib also casts the results to typeof(Obj), but only if + * + * ( defined(g_has_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_56 ) + * + * Since we build NetworkManager with older GLIB_VERSION_MAX_ALLOWED, it's + * not taking effect. + * + * Override this. */ +#undef g_object_ref +#undef g_object_ref_sink #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 /*****************************************************************************/ diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c index 6e728e6b..a6158269 100644 --- a/shared/nm-glib-aux/nm-hash-utils.c +++ b/shared/nm-glib-aux/nm-hash-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -45,7 +44,10 @@ _get_hash_key_init (void) * to use it as guint* or guint64* pointer. */ static union { guint8 v8[HASH_KEY_SIZE]; - } g_arr _nm_alignas (guint64); + guint _align_as_uint; + guint32 _align_as_uint32; + guint64 _align_as_uint64; + } g_arr; const guint8 *g; union { guint8 v8[HASH_KEY_SIZE]; @@ -125,14 +127,17 @@ void nm_hash_siphash42_init (CSipHash *h, guint static_seed) { const guint8 *g; - guint seed[HASH_KEY_SIZE_GUINT]; + union { + guint64 _align_as_uint64; + guint arr[HASH_KEY_SIZE_GUINT]; + } seed; nm_assert (h); g = _get_hash_key (); - memcpy (seed, g, HASH_KEY_SIZE); - seed[0] ^= static_seed; - c_siphash_init (h, (const guint8 *) seed); + memcpy (&seed, g, HASH_KEY_SIZE); + seed.arr[0] ^= static_seed; + c_siphash_init (h, (const guint8 *) &seed); } guint @@ -194,3 +199,25 @@ nm_pstr_equal (gconstpointer a, gconstpointer b) && s2 && nm_streq0 (*s1, *s2)); } + +guint +nm_pdirect_hash (gconstpointer p) +{ + const void *const*s = p; + + if (!s) + return nm_hash_static (1852748873u); + return nm_direct_hash (*s); +} + +gboolean +nm_pdirect_equal (gconstpointer a, gconstpointer b) +{ + const void *const*s1 = a; + const void *const*s2 = b; + + return (s1 == s2) + || ( s1 + && s2 + && *s1 == *s2); +} diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h index 3f622f99..f13e0b6d 100644 --- a/shared/nm-glib-aux/nm-hash-utils.h +++ b/shared/nm-glib-aux/nm-hash-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -34,7 +33,7 @@ void nm_hash_siphash42_init (CSipHash *h, guint static_seed); * * Note, that this is guaranteed to use siphash42 under the hood (contrary to * all other NMHash API, which leave this undefined). That matters at the point, - * where the caller needs to be sure that a reasonably strong hasing algorithm + * where the caller needs to be sure that a reasonably strong hashing algorithm * is used. (Yes, NMHash is all about siphash24, but otherwise that is not promised * anywhere). * @@ -291,7 +290,16 @@ gboolean nm_pstr_equal (gconstpointer a, gconstpointer b); /*****************************************************************************/ -#define NM_HASH_OBFUSCATE_PTR_FMT "%016llx" +/* this hashes/compares the pointer value that we point to. Basically, + * (((const void *const*) a) == ((const void *const*) b)). */ + +guint nm_pdirect_hash (gconstpointer p); + +gboolean nm_pdirect_equal (gconstpointer a, gconstpointer b); + +/*****************************************************************************/ + +#define NM_HASH_OBFUSCATE_PTR_FMT "%016" G_GINT64_MODIFIER "x" /* sometimes we want to log a pointer directly, for providing context/information about * the message that get logged. Logging pointer values directly defeats ASLR, so we should @@ -307,9 +315,19 @@ gboolean nm_pstr_equal (gconstpointer a, gconstpointer b); \ nm_hash_init (&_h, (static_seed)); \ nm_hash_update_val (&_h, _val_obf_ptr); \ - (unsigned long long) nm_hash_complete_u64 (&_h); \ + nm_hash_complete_u64 (&_h); \ }) +/* if you want to log obfuscated pointer for a certain context (like, NMPRuleManager + * logging user-tags), then you are advised to use nm_hash_obfuscate_ptr() with your + * own, unique static-seed. + * + * However, for example the singleton constructors log the obfuscated pointer values + * for all singletons, so they must all be obfuscated with the same seed. So, this + * macro uses a particular static seed that should be used by when comparing pointer + * values in a global context. */ +#define NM_HASH_OBFUSCATE_PTR(ptr) (nm_hash_obfuscate_ptr (1678382159u, ptr)) + /*****************************************************************************/ #endif /* __NM_HASH_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c index 51312748..23133ec5 100644 --- a/shared/nm-glib-aux/nm-io-utils.c +++ b/shared/nm-glib-aux/nm-io-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -437,3 +436,26 @@ nm_utils_file_set_contents (const char *filename, return TRUE; } + +/** + * nm_utils_file_stat: + * @filename: the filename to stat. + * @out_st: (allow-none) (out): if given, this will be passed to stat(). + * + * Just wraps stat() and gives the errno number as function result instead + * of setting the errno (though, errno is also set). It's only for convenience + * with + * + * if (nm_utils_file_stat (filename, NULL) == -ENOENT) { + * } + * + * Returns: 0 on success a negative errno on failure. */ +int +nm_utils_file_stat (const char *filename, struct stat *out_st) +{ + struct stat st; + + if (stat (filename, out_st ?: &st) != 0) + return -NM_ERRNO_NATIVE (errno); + return 0; +} diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h index dc72a2a6..121fc481 100644 --- a/shared/nm-glib-aux/nm-io-utils.h +++ b/shared/nm-glib-aux/nm-io-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -60,4 +59,8 @@ gboolean nm_utils_file_set_contents (const char *filename, mode_t mode, GError **error); +struct stat; + +int nm_utils_file_stat (const char *filename, struct stat *out_st); + #endif /* __NM_IO_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h index 5a73231f..d4642319 100644 --- a/shared/nm-glib-aux/nm-jansson.h +++ b/shared/nm-glib-aux/nm-jansson.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +43,106 @@ NM_AUTO_DEFINE_FCN0 (json_t *, _nm_auto_decref_json, json_decref) #define nm_auto_decref_json nm_auto(_nm_auto_decref_json) +/*****************************************************************************/ + +static inline int +nm_jansson_json_as_bool (const json_t *elem, + bool *out_val) +{ + if (!elem) + return 0; + + if (!json_is_boolean (elem)) + return -EINVAL; + + NM_SET_OUT (out_val, json_boolean_value (elem)); + return 1; +} + +static inline int +nm_jansson_json_as_int32 (const json_t *elem, + gint32 *out_val) +{ + json_int_t v; + + if (!elem) + return 0; + + if (!json_is_integer (elem)) + return -EINVAL; + + v = json_integer_value (elem); + if ( v < (gint64) G_MININT32 + || v > (gint64) G_MAXINT32) + return -ERANGE; + + NM_SET_OUT (out_val, v); + return 1; +} + +static inline int +nm_jansson_json_as_int (const json_t *elem, + int *out_val) +{ + json_int_t v; + + if (!elem) + return 0; + + if (!json_is_integer (elem)) + return -EINVAL; + + v = json_integer_value (elem); + if ( v < (gint64) G_MININT + || v > (gint64) G_MAXINT) + return -ERANGE; + + NM_SET_OUT (out_val, v); + return 1; +} + +static inline int +nm_jansson_json_as_string (const json_t *elem, + const char **out_val) +{ + if (!elem) + return 0; + + if (!json_is_string (elem)) + return -EINVAL; + + NM_SET_OUT (out_val, json_string_value (elem)); + return 1; +} + +/*****************************************************************************/ + +#ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS +#include "nm-value-type.h" +static inline gboolean +nm_value_type_from_json (NMValueType value_type, + const json_t *elem, + gpointer out_val) +{ + switch (value_type) { + case NM_VALUE_TYPE_BOOL: return (nm_jansson_json_as_bool (elem, out_val) > 0); + case NM_VALUE_TYPE_INT32: return (nm_jansson_json_as_int32 (elem, out_val) > 0); + case NM_VALUE_TYPE_INT: return (nm_jansson_json_as_int (elem, out_val) > 0); + + /* warning: this overwrites/leaks the previous value. You better have *out_val + * point to uninitialized memory or NULL. */ + case NM_VALUE_TYPE_STRING: return (nm_jansson_json_as_string (elem, out_val) > 0); + + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); + return FALSE; +} +#endif + +/*****************************************************************************/ + #endif /* WITH_JANSON */ #endif /* __NM_JANSSON_H__ */ diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c new file mode 100644 index 00000000..6f04ef2b --- /dev/null +++ b/shared/nm-glib-aux/nm-json-aux.c @@ -0,0 +1,149 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-json-aux.h" + +/*****************************************************************************/ + +static void +_gstr_append_string_len (GString *gstr, + const char *str, + gsize len) +{ + g_string_append_c (gstr, '\"'); + + while (len > 0) { + gsize n; + const char *end; + gboolean valid; + + nm_assert (len > 0); + + valid = g_utf8_validate (str, len, &end); + + nm_assert ( end + && end >= str + && end <= &str[len]); + + if (end > str) { + const char *s; + + for (s = str; s < end; s++) { + nm_assert (s[0] != '\0'); + + if (s[0] < 0x20) { + const char *text; + + switch (s[0]) { + case '\\': text = "\\\\"; break; + case '\"': text = "\\\""; break; + case '\b': text = "\\b"; break; + case '\f': text = "\\f"; break; + case '\n': text = "\\n"; break; + case '\r': text = "\\r"; break; + case '\t': text = "\\t"; break; + default: + g_string_append_printf (gstr, "\\u%04X", (guint) s[0]); + continue; + } + g_string_append (gstr, text); + continue; + } + + if (NM_IN_SET (s[0], '\\', '\"')) + g_string_append_c (gstr, '\\'); + g_string_append_c (gstr, s[0]); + } + } else + nm_assert (!valid); + + if (valid) { + nm_assert (end == &str[len]); + break; + } + + nm_assert (end < &str[len]); + + if (end[0] == '\0') { + /* there is a NUL byte in the string. Technically this is valid UTF-8, so we + * encode it there. However, this will likely result in a truncated string when + * parsing. */ + g_string_append (gstr, "\\u0000"); + } else { + /* the character is not valid UTF-8. There is nothing we can do about it, because + * JSON can only contain UTF-8 and even the escape sequences can only escape Unicode + * codepoints (but not binary). + * + * The argument is not a a string (in any known encoding), hence we cannot represent + * it as a JSON string (which are unicode strings). + * + * Print an underscore instead of the invalid char :) */ + g_string_append_c (gstr, '_'); + } + + n = str - end; + nm_assert (n < len); + n++; + str += n; + len -= n; + } + + g_string_append_c (gstr, '\"'); +} + +void +nm_json_aux_gstr_append_string_len (GString *gstr, + const char *str, + gsize n) +{ + g_return_if_fail (gstr); + + _gstr_append_string_len (gstr, str, n); +} + +void +nm_json_aux_gstr_append_string (GString *gstr, + const char *str) +{ + g_return_if_fail (gstr); + + if (!str) + g_string_append (gstr, "null"); + else + _gstr_append_string_len (gstr, str, strlen (str)); +} + +void +nm_json_aux_gstr_append_obj_name (GString *gstr, + const char *key, + char start_container) +{ + g_return_if_fail (gstr); + g_return_if_fail (key); + + nm_json_aux_gstr_append_string (gstr, key); + + if (start_container != '\0') { + nm_assert (NM_IN_SET (start_container, '[', '{')); + g_string_append_printf (gstr, ": %c ", start_container); + } else + g_string_append (gstr, ": "); +} diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h new file mode 100644 index 00000000..19d43ce4 --- /dev/null +++ b/shared/nm-glib-aux/nm-json-aux.h @@ -0,0 +1,83 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_JSON_AUX_H__ +#define __NM_JSON_AUX_H__ + +/*****************************************************************************/ + +static inline GString * +nm_json_aux_gstr_append_delimiter (GString *gstr) +{ + g_string_append (gstr, ", "); + return gstr; +} + +void nm_json_aux_gstr_append_string_len (GString *gstr, + const char *str, + gsize n); + +void nm_json_aux_gstr_append_string (GString *gstr, + const char *str); + +static inline void +nm_json_aux_gstr_append_bool (GString *gstr, + gboolean v) +{ + g_string_append (gstr, v ? "true" : "false"); +} + +static inline void +nm_json_aux_gstr_append_int64 (GString *gstr, + gint64 v) +{ + g_string_append_printf (gstr, "%"G_GINT64_FORMAT, v); +} + +void nm_json_aux_gstr_append_obj_name (GString *gstr, + const char *key, + char start_container); + +/*****************************************************************************/ + +#ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS +#include "nm-value-type.h" +static inline void +nm_value_type_to_json (NMValueType value_type, + GString *gstr, + gconstpointer p_field) +{ + nm_assert (p_field); + nm_assert (gstr); + + switch (value_type) { + case NM_VALUE_TYPE_BOOL: nm_json_aux_gstr_append_bool (gstr, *((const bool *) p_field)); return; + case NM_VALUE_TYPE_INT32: nm_json_aux_gstr_append_int64 (gstr, *((const gint32 *) p_field)); return; + case NM_VALUE_TYPE_INT: nm_json_aux_gstr_append_int64 (gstr, *((const int *) p_field)); return; + case NM_VALUE_TYPE_STRING: nm_json_aux_gstr_append_string (gstr, *((const char *const *) p_field)); return; + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); +} +#endif + +/*****************************************************************************/ + +#endif /* __NM_JSON_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c new file mode 100644 index 00000000..0257bcca --- /dev/null +++ b/shared/nm-glib-aux/nm-keyfile-aux.c @@ -0,0 +1,413 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-keyfile-aux.h" + +#include <syslog.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "nm-io-utils.h" + +/*****************************************************************************/ + +struct _NMKeyFileDB { + NMKeyFileDBLogFcn log_fcn; + NMKeyFileDBGotDirtyFcn got_dirty_fcn; + gpointer user_data; + const char *group_name; + GKeyFile *kf; + guint ref_count; + + bool is_started:1; + bool dirty:1; + bool destroyed:1; + + char filename[]; +}; + +#define _NMLOG(self, \ + syslog_level, \ + fmt, \ + ...) \ + G_STMT_START { \ + NMKeyFileDB *_self = (self); \ + \ + nm_assert (_self); \ + nm_assert (!_self->destroyed); \ + \ + if (_self->log_fcn) { \ + _self->log_fcn (_self, \ + (syslog_level), \ + _self->user_data, \ + ""fmt"", \ + ##__VA_ARGS__); \ + }; \ + } G_STMT_END + +#define _LOGD(...) _NMLOG (self, LOG_DEBUG, __VA_ARGS__) + +static gboolean +_IS_KEY_FILE_DB (NMKeyFileDB *self, gboolean require_is_started, gboolean allow_destroyed) +{ + if (self == NULL) + return FALSE; + if (self->ref_count <= 0) { + nm_assert_not_reached (); + return FALSE; + } + if ( require_is_started + && !self->is_started) + return FALSE; + if ( !allow_destroyed + && self->destroyed) + return FALSE; + return TRUE; +} + +/*****************************************************************************/ + +NMKeyFileDB * +nm_key_file_db_new (const char *filename, + const char *group_name, + NMKeyFileDBLogFcn log_fcn, + NMKeyFileDBGotDirtyFcn got_dirty_fcn, + gpointer user_data) +{ + NMKeyFileDB *self; + gsize l_filename; + gsize l_group; + + g_return_val_if_fail (filename && filename[0], NULL); + g_return_val_if_fail (group_name && group_name[0], NULL); + + l_filename = strlen (filename); + l_group = strlen (group_name); + + self = g_malloc0 (sizeof (NMKeyFileDB) + l_filename + 1 + l_group + 1); + self->ref_count = 1; + self->log_fcn = log_fcn; + self->got_dirty_fcn = got_dirty_fcn; + self->user_data = user_data; + self->kf = g_key_file_new (); + g_key_file_set_list_separator (self->kf, ','); + memcpy (self->filename, filename, l_filename + 1); + self->group_name = &self->filename[l_filename + 1]; + memcpy ((char *) self->group_name, group_name, l_group + 1); + + return self; +} + +NMKeyFileDB * +nm_key_file_db_ref (NMKeyFileDB *self) +{ + if (!self) + return NULL; + + g_return_val_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE), NULL); + + nm_assert (self->ref_count <= G_MAXUINT); + self->ref_count++; + return self; +} + +void +nm_key_file_db_unref (NMKeyFileDB *self) +{ + if (!self) + return; + + g_return_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE)); + + if (--self->ref_count > 0) + return; + + g_key_file_unref (self->kf); + + g_free (self); +} + +/* destroy() is like unref, but it also makes the instance unusable. + * All changes afterwards fail with an assertion. + * + * The point is that NMKeyFileDB is ref-counted in principle. But there + * is a primary owner who also provides the log_fcn(). + * + * When the primary owner goes out of scope and gives up the reference, it does + * not want to receive any log notifications anymore. + * + * The way NMKeyFileDB is intended to be used is in a very strict context: + * NMSettings owns the NMKeyFileDB instance and receives logging notifications. + * It's also the last one to persist the data to disk. Afterwards, no other user + * is supposed to be around and do anything with NMKeyFileDB. But since NMKeyFileDB + * is ref-counted it's hard to ensure that this is truly honored. So we start + * asserting at that point. + */ +void +nm_key_file_db_destroy (NMKeyFileDB *self) +{ + if (!self) + return; + + g_return_if_fail (_IS_KEY_FILE_DB (self, FALSE, FALSE)); + g_return_if_fail (!self->destroyed); + + self->destroyed = TRUE; + nm_key_file_db_unref (self); +} + +/*****************************************************************************/ + +/* nm_key_file_db_start() is supposed to be called right away, after creating the + * instance. + * + * It's not done as separate step after nm_key_file_db_new(), because we want to log, + * and the log_fcn returns the self pointer (which we should not expose before + * nm_key_file_db_new() returns. */ +void +nm_key_file_db_start (NMKeyFileDB *self) +{ + int r; + gs_free char *contents = NULL; + gsize contents_len; + gs_free_error GError *error = NULL; + + g_return_if_fail (_IS_KEY_FILE_DB (self, FALSE, FALSE)); + g_return_if_fail (!self->is_started); + + self->is_started = TRUE; + + r = nm_utils_file_get_contents (-1, + self->filename, + 20*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, + &contents_len, + &error); + if (r < 0) { + _LOGD ("failed to read \"%s\": %s", self->filename, error->message); + return; + } + + if (!g_key_file_load_from_data (self->kf, + contents, + contents_len, + G_KEY_FILE_KEEP_COMMENTS, + &error)) { + _LOGD ("failed to load keyfile \"%s\": %s", self->filename, error->message); + return; + } + + _LOGD ("loaded keyfile-db for \"%s\"", self->filename); +} + +/*****************************************************************************/ + +const char * +nm_key_file_db_get_filename (NMKeyFileDB *self) +{ + g_return_val_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE), NULL); + + return self->filename; +} + +gboolean +nm_key_file_db_is_dirty (NMKeyFileDB *self) +{ + g_return_val_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE), FALSE); + + return self->dirty; +} + +/*****************************************************************************/ + +char * +nm_key_file_db_get_value (NMKeyFileDB *self, + const char *key) +{ + g_return_val_if_fail (_IS_KEY_FILE_DB (self, TRUE, TRUE), NULL); + + return g_key_file_get_value (self->kf, self->group_name, key, NULL); +} + +char ** +nm_key_file_db_get_string_list (NMKeyFileDB *self, + const char *key, + gsize *out_len) +{ + g_return_val_if_fail (_IS_KEY_FILE_DB (self, TRUE, TRUE), NULL); + + return g_key_file_get_string_list (self->kf, self->group_name, key, out_len, NULL); +} + +/*****************************************************************************/ + +static void +_got_dirty (NMKeyFileDB *self, + const char *key) +{ + nm_assert (_IS_KEY_FILE_DB (self, TRUE, FALSE)); + nm_assert (!self->dirty); + + _LOGD ("updated entry for %s.%s", self->group_name, key); + + self->dirty = TRUE; + if (self->got_dirty_fcn) + self->got_dirty_fcn (self, self->user_data); +} + +/*****************************************************************************/ + +void +nm_key_file_db_remove_key (NMKeyFileDB *self, + const char *key) +{ + gboolean got_dirty = FALSE; + + g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE)); + + if (!key) + return; + + if (!self->dirty) { + gs_free_error GError *error = NULL; + + g_key_file_has_key (self->kf, self->group_name, key, &error); + got_dirty = (error != NULL); + } + g_key_file_remove_key (self->kf, self->group_name, key, NULL); + + if (got_dirty) + _got_dirty (self, key); +} + +void +nm_key_file_db_set_value (NMKeyFileDB *self, + const char *key, + const char *value) +{ + gs_free char *old_value = NULL; + gboolean got_dirty = FALSE; + + g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE)); + g_return_if_fail (key); + + if (!value) { + nm_key_file_db_remove_key (self, key); + return; + } + + if (!self->dirty) { + gs_free_error GError *error = NULL; + + old_value = g_key_file_get_value (self->kf, self->group_name, key, &error); + if (error) + got_dirty = TRUE; + } + + g_key_file_set_value (self->kf, self->group_name, key, value); + + if ( !self->dirty + && !got_dirty) { + gs_free_error GError *error = NULL; + gs_free char *new_value = NULL; + + new_value = g_key_file_get_value (self->kf, self->group_name, key, &error); + if ( error + || !new_value + || !nm_streq0 (old_value, new_value)) + got_dirty = TRUE; + } + + if (got_dirty) + _got_dirty (self, key); +} + +void +nm_key_file_db_set_string_list (NMKeyFileDB *self, + const char *key, + const char *const*value, + gssize len) +{ + gs_free char *old_value = NULL; + gboolean got_dirty = FALSE;; + + g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE)); + g_return_if_fail (key); + + if (!value) { + nm_key_file_db_remove_key (self, key); + return; + } + + if (!self->dirty) { + gs_free_error GError *error = NULL; + + old_value = g_key_file_get_value (self->kf, self->group_name, key, &error); + if (error) + got_dirty = TRUE; + } + + if (len < 0) + len = NM_PTRARRAY_LEN (value); + + g_key_file_set_string_list (self->kf, self->group_name, key, value, len); + + if ( !self->dirty + && !got_dirty) { + gs_free_error GError *error = NULL; + gs_free char *new_value = NULL; + + new_value = g_key_file_get_value (self->kf, self->group_name, key, &error); + if ( error + || !new_value + || !nm_streq0 (old_value, new_value)) + got_dirty = TRUE; + } + + if (got_dirty) + _got_dirty (self, key); +} + +/*****************************************************************************/ + +void +nm_key_file_db_to_file (NMKeyFileDB *self, + gboolean force) +{ + gs_free_error GError *error = NULL; + + g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE)); + + if ( !force + && !self->dirty) + return; + + self->dirty = FALSE; + + if (!g_key_file_save_to_file (self->kf, + self->filename, + &error)) { + _LOGD ("failure to write keyfile \"%s\": %s", self->filename, error->message); + } else + _LOGD ("write keyfile: \"%s\"", self->filename); +} diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h new file mode 100644 index 00000000..8563f4d1 --- /dev/null +++ b/shared/nm-glib-aux/nm-keyfile-aux.h @@ -0,0 +1,78 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_KEYFILE_AUX_H__ +#define __NM_KEYFILE_AUX_H__ + +/*****************************************************************************/ + +typedef struct _NMKeyFileDB NMKeyFileDB; + +typedef void (*NMKeyFileDBLogFcn) (NMKeyFileDB *self, + int syslog_level, + gpointer user_data, + const char *fmt, + ...) G_GNUC_PRINTF (4, 5); + +typedef void (*NMKeyFileDBGotDirtyFcn) (NMKeyFileDB *self, + gpointer user_data); + +NMKeyFileDB *nm_key_file_db_new (const char *filename, + const char *group, + NMKeyFileDBLogFcn log_fcn, + NMKeyFileDBGotDirtyFcn got_dirty_fcn, + gpointer user_data); + +void nm_key_file_db_start (NMKeyFileDB *self); + +NMKeyFileDB *nm_key_file_db_ref (NMKeyFileDB *self); +void nm_key_file_db_unref (NMKeyFileDB *self); + +void nm_key_file_db_destroy (NMKeyFileDB *self); + +const char *nm_key_file_db_get_filename (NMKeyFileDB *self); + +gboolean nm_key_file_db_is_dirty (NMKeyFileDB *self); + +char *nm_key_file_db_get_value (NMKeyFileDB *self, + const char *key); + +char **nm_key_file_db_get_string_list (NMKeyFileDB *self, + const char *key, + gsize *out_len); + +void nm_key_file_db_remove_key (NMKeyFileDB *self, + const char *key); + +void nm_key_file_db_set_value (NMKeyFileDB *self, + const char *key, + const char *value); + +void nm_key_file_db_set_string_list (NMKeyFileDB *self, + const char *key, + const char *const*value, + gssize len); + +void nm_key_file_db_to_file (NMKeyFileDB *self, + gboolean force); + +/*****************************************************************************/ + +#endif /* __NM_KEYFILE_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h index 900dfff8..c60a20b5 100644 --- a/shared/nm-glib-aux/nm-logging-fwd.h +++ b/shared/nm-glib-aux/nm-logging-fwd.h @@ -110,4 +110,31 @@ void _nm_log_impl (const char *file, const char *fmt, ...) _nm_printf (10, 11); +static inline NMLogLevel +nm_log_level_from_syslog (int syslog_level) +{ + switch (syslog_level) { + case 0 /* LOG_EMERG */ : return LOGL_ERR; + case 1 /* LOG_ALERT */ : return LOGL_ERR; + case 2 /* LOG_CRIT */ : return LOGL_ERR; + case 3 /* LOG_ERR */ : return LOGL_ERR; + case 4 /* LOG_WARNING */ : return LOGL_WARN; + case 5 /* LOG_NOTICE */ : return LOGL_INFO; + case 6 /* LOG_INFO */ : return LOGL_DEBUG; + case 7 /* LOG_DEBUG */ : return LOGL_TRACE; + default: + return syslog_level >= 0 ? LOGL_TRACE : LOGL_ERR; + } +} + +/*****************************************************************************/ + +struct timespec; + +/* this function must be implemented to handle the notification when + * the first monotonic-timestamp is fetched. */ +extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp, + gint64 offset_sec, + gboolean is_boottime); + #endif /* __NM_LOGGING_DEFINES_H__ */ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index 2e46cd2d..9502c442 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -635,8 +634,14 @@ NM_G_ERROR_MSG (GError *error) * It's useful to check the let the compiler ensure that @value is * of a certain type. */ #define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value))) +#define _NM_ENSURE_TYPE_CONST(type, value) (_Generic ((value), \ + const type : ((const type) (value)), \ + const type const: ((const type) (value)), \ + type : ((const type) (value)), \ + type const: ((const type) (value)))) #else #define _NM_ENSURE_TYPE(type, value) (value) +#define _NM_ENSURE_TYPE_CONST(type, value) ((const type) (value)) #endif #if _NM_CC_SUPPORT_GENERIC @@ -865,6 +870,28 @@ fcn (void) \ /*****************************************************************************/ +static inline int +nm_strcmp0 (const char *s1, const char *s2) +{ + int c; + + /* like g_strcmp0(), but this is inlinable. + * + * Also, it is guaranteed to return either -1, 0, or 1. */ + if (s1 == s2) + return 0; + if (!s1) + return -1; + if (!s2) + return 1; + c = strcmp (s1, s2); + if (c < 0) + return -1; + if (c > 0) + return 1; + return 0; +} + static inline gboolean nm_streq (const char *s1, const char *s2) { @@ -879,14 +906,19 @@ nm_streq0 (const char *s1, const char *s2) } #define NM_STR_HAS_PREFIX(str, prefix) \ - (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0) + ({ \ + const char *const _str = (str); \ + \ + _str && (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0); \ + }) #define NM_STR_HAS_SUFFIX(str, suffix) \ ({ \ - const char *_str = (str); \ - gsize _l = strlen (_str); \ + const char *_str; \ + gsize _l; \ \ - ( (_l >= NM_STRLEN (suffix)) \ + ( (_str = (str)) \ + && ((_l = strlen (_str)) >= NM_STRLEN (suffix)) \ && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ ""suffix"", \ NM_STRLEN (suffix)) == 0)); \ @@ -989,10 +1021,9 @@ typedef enum { \ } _PropertyEnums; \ static GParamSpec *obj_properties[_PROPERTY_ENUMS_LAST] = { NULL, } -#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ -NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \ +#define NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY(obj_type, obj_properties, property_enums_type, prop_0) \ static inline void \ -_nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums *props) \ +_nm_gobject_notify_together_impl (obj_type *obj, guint n, const property_enums_type *props) \ { \ const gboolean freeze_thaw = (n > 1); \ \ @@ -1002,9 +1033,9 @@ _nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums * if (freeze_thaw) \ g_object_freeze_notify ((GObject *) obj); \ while (n-- > 0) { \ - const _PropertyEnums prop = *props++; \ + const property_enums_type prop = *props++; \ \ - if (prop != PROP_0) { \ + 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]); \ @@ -1015,11 +1046,15 @@ _nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums * } \ \ static inline void \ -_notify (obj_type *obj, _PropertyEnums prop) \ +_notify (obj_type *obj, property_enums_type prop) \ { \ _nm_gobject_notify_together_impl (obj, 1, &prop); \ } \ +#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ +NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \ +NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY (obj_type, obj_properties, _PropertyEnums, PROP_0) + /* 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. */ @@ -1130,6 +1165,30 @@ nm_g_object_unref (gpointer obj) #define nm_clear_g_object(pp) \ nm_clear_pointer (pp, g_object_unref) +/** + * nm_clear_error: + * @err: a pointer to pointer to a #GError. + * + * This is like g_clear_error(). The only difference is + * that this is an inline function. + */ +static inline void +nm_clear_error (GError **err) +{ + if (err && *err) { + g_error_free (*err); + *err = NULL; + } +} + +/* Patch g_clear_error() to use nm_clear_error(), which is inlineable + * and visible to the compiler. For example gs_free_error attribute only + * frees the error after checking that it's not %NULL. So, in many cases + * the compiler knows that gs_free_error has no effect and can optimize + * the call away. By making g_clear_error() inlineable, we give the compiler + * more chance to detect that the function actually has no effect. */ +#define g_clear_error(ptr) nm_clear_error(ptr) + static inline gboolean nm_clear_g_source (guint *id) { @@ -1219,6 +1278,14 @@ nm_g_variant_ref (GVariant *v) return v; } +static inline GVariant * +nm_g_variant_ref_sink (GVariant *v) +{ + if (v) + g_variant_ref_sink (v); + return v; +} + static inline void nm_g_variant_unref (GVariant *v) { @@ -1226,6 +1293,14 @@ nm_g_variant_unref (GVariant *v) g_variant_unref (v); } +static inline GVariant * +nm_g_variant_take_ref (GVariant *v) +{ + if (v) + g_variant_take_ref (v); + return v; +} + /*****************************************************************************/ /* Determine whether @x is a power of two (@x being an integer type). @@ -1494,6 +1569,11 @@ nm_strcmp_p (gconstpointer a, gconstpointer b) /*****************************************************************************/ +#define nm_g_slice_free(ptr) \ + g_slice_free (typeof (*(ptr)), ptr) + +/*****************************************************************************/ + /* 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 @@ -1523,15 +1603,72 @@ nm_memdup (gconstpointer data, gsize size) return p; } +#define nm_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ + ({ \ + const gsize _bytes = (bytes); \ + typeof (to_free) _to_free = (to_free); \ + typeof (*_to_free) _ptr; \ + \ + G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 500); \ + nm_assert (_to_free && !*_to_free); \ + \ + if (_bytes <= (alloca_maxlen)) { \ + _ptr = g_alloca (_bytes); \ + } else { \ + _ptr = g_malloc (_bytes); \ + *_to_free = _ptr; \ + }; \ + \ + _ptr; \ + }) + +#define nm_malloc0_maybe_a(alloca_maxlen, bytes, to_free) \ + ({ \ + const gsize _bytes = (bytes); \ + typeof (to_free) _to_free = (to_free); \ + typeof (*_to_free) _ptr; \ + \ + G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 500); \ + nm_assert (_to_free && !*_to_free); \ + \ + if (_bytes <= (alloca_maxlen)) { \ + _ptr = g_alloca (_bytes); \ + memset (_ptr, 0, _bytes); \ + } else { \ + _ptr = g_malloc0 (_bytes); \ + *_to_free = _ptr; \ + }; \ + \ + _ptr; \ + }) + +#define nm_memdup_maybe_a(alloca_maxlen, data, size, to_free) \ + ({ \ + const gsize _size = (size); \ + typeof (to_free) _to_free_md = (to_free); \ + typeof (*_to_free_md) _ptr_md = NULL; \ + \ + nm_assert (_to_free_md && !*_to_free_md); \ + \ + if (_size > 0u) { \ + _ptr_md = nm_malloc_maybe_a ((alloca_maxlen), _size, _to_free_md); \ + memcpy (_ptr_md, (data), _size); \ + } \ + \ + _ptr_md; \ + }) + static inline char * _nm_strndup_a_step (char *s, const char *str, gsize len) { NM_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation"); + NM_PRAGMA_WARNING_DISABLE ("-Wstringop-overflow"); if (len > 0) strncpy (s, str, len); s[len] = '\0'; return s; NM_PRAGMA_WARNING_REENABLE; + NM_PRAGMA_WARNING_REENABLE; } /* Similar to g_strndup(), however, if the string (including the terminating diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h index 4edd1f3e..06016bdd 100644 --- a/shared/nm-glib-aux/nm-obj.h +++ b/shared/nm-glib-aux/nm-obj.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c index d7c7da42..f56f8b99 100644 --- a/shared/nm-glib-aux/nm-random-utils.c +++ b/shared/nm-glib-aux/nm-random-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h index 15a118d3..8e134ee9 100644 --- a/shared/nm-glib-aux/nm-random-utils.h +++ b/shared/nm-glib-aux/nm-random-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c index 81f8b5ae..aeb88877 100644 --- a/shared/nm-glib-aux/nm-secret-utils.c +++ b/shared/nm-glib-aux/nm-secret-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h index 034ef7bd..0fd1ac8b 100644 --- a/shared/nm-glib-aux/nm-secret-utils.h +++ b/shared/nm-glib-aux/nm-secret-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index cf08a77f..c8a253a6 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -734,10 +733,7 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma gint64 v; const char *s = NULL; - if (str) { - while (g_ascii_isspace (str[0])) - str++; - } + str = nm_str_skip_leading_spaces (str); if (!str || !str[0]) { errno = EINVAL; return fallback; @@ -748,9 +744,9 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma if (errno != 0) return fallback; + if (s[0] != '\0') { - while (g_ascii_isspace (s[0])) - s++; + s = nm_str_skip_leading_spaces (s); if (s[0] != '\0') { errno = EINVAL; return fallback; @@ -810,6 +806,15 @@ _nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 /*****************************************************************************/ +int +nm_strcmp_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *s1 = a; + const char *s2 = 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 @@ -827,6 +832,15 @@ nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) } int +nm_strcmp0_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return nm_strcmp0 (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); @@ -1350,6 +1364,8 @@ _nm_utils_strv_cleanup (char **strv, return strv; if (strip_whitespace) { + /* we only modify the strings pointed to by @strv if @strip_whitespace is + * requested. Otherwise, the strings themselves are untouched. */ for (i = 0; strv[i]; i++) g_strstrip (strv[i]); } @@ -2130,6 +2146,8 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) return 0; } +/*****************************************************************************/ + NMUtilsNamedValue * nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len) { @@ -2154,15 +2172,106 @@ nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_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_utils_named_value_list_sort (values, len, NULL, NULL); NM_SET_OUT (out_len, len); return values; } +gssize +nm_utils_named_value_list_find (const NMUtilsNamedValue *arr, + gsize len, + const char *name, + gboolean sorted) +{ + gsize i; + + nm_assert (name); + +#if NM_MORE_ASSERTS > 5 + { + for (i = 0; i < len; i++) { + const NMUtilsNamedValue *v = &arr[i]; + + nm_assert (v->name); + if ( sorted + && i > 0) + nm_assert (strcmp (arr[i - 1].name, v->name) < 0); + } + } + + nm_assert ( !sorted + || nm_utils_named_value_list_is_sorted (arr, len, FALSE, NULL, NULL)); +#endif + + if (sorted) { + return nm_utils_array_find_binary_search (arr, + sizeof (NMUtilsNamedValue), + len, + &name, + nm_strcmp_p_with_data, + NULL); + } + for (i = 0; i < len; i++) { + if (nm_streq (arr[i].name, name)) + return i; + } + return ~((gssize) len); +} + +gboolean +nm_utils_named_value_list_is_sorted (const NMUtilsNamedValue *arr, + gsize len, + gboolean accept_duplicates, + GCompareDataFunc compare_func, + gpointer user_data) +{ + gsize i; + int c_limit; + + if (len == 0) + return TRUE; + + g_return_val_if_fail (arr, FALSE); + + if (!compare_func) + compare_func = nm_strcmp_p_with_data; + + c_limit = accept_duplicates ? 0 : -1; + + for (i = 1; i < len; i++) { + int c; + + c = compare_func (&arr[i - 1], &arr[i], user_data); + if (c > c_limit) + return FALSE; + } + return TRUE; +} + +void +nm_utils_named_value_list_sort (NMUtilsNamedValue *arr, + gsize len, + GCompareDataFunc compare_func, + gpointer user_data) +{ + if (len == 0) + return; + + g_return_if_fail (arr); + + if (len == 1) + return; + + g_qsort_with_data (arr, + len, + sizeof (NMUtilsNamedValue), + compare_func ?: nm_strcmp_p_with_data, + user_data); +} + +/*****************************************************************************/ + gpointer * nm_utils_hash_keys_to_array (GHashTable *hash, GCompareDataFunc compare_func, @@ -2193,12 +2302,41 @@ nm_utils_hash_keys_to_array (GHashTable *hash, return keys; } +gboolean +nm_utils_hashtable_same_keys (const GHashTable *a, + const GHashTable *b) +{ + GHashTableIter h; + const char *k; + + if (a == b) + return TRUE; + if (!a || !b) + return FALSE; + if (g_hash_table_size ((GHashTable *) a) != g_hash_table_size ((GHashTable *) b)) + return FALSE; + + g_hash_table_iter_init (&h, (GHashTable *) a); + while (g_hash_table_iter_next (&h, (gpointer) &k, NULL)) { + if (!g_hash_table_contains ((GHashTable *) b, k)) + return FALSE; + } + +#if NM_MORE_ASSERTS > 5 + g_hash_table_iter_init (&h, (GHashTable *) b); + while (g_hash_table_iter_next (&h, (gpointer) &k, NULL)) + nm_assert (g_hash_table_contains ((GHashTable *) a, k)); +#endif + + return TRUE; +} + char ** nm_utils_strv_make_deep_copied (const char **strv) { gsize i; - /* it takes a strv dictionary, and copies each + /* it takes a strv list, and copies each * strings. Note that this updates @strv *in-place* * and returns it. */ @@ -2210,6 +2348,79 @@ nm_utils_strv_make_deep_copied (const char **strv) return (char **) strv; } +char ** +nm_utils_strv_make_deep_copied_n (const char **strv, gsize len) +{ + gsize i; + + /* it takes a strv array with len elements, and copies each + * strings. Note that this updates @strv *in-place* + * and returns it. */ + + if (!strv) + return NULL; + for (i = 0; i < len; i++) + strv[i] = g_strdup (strv[i]); + + return (char **) strv; +} + +/** + * @strv: the strv array to copy. It may be %NULL if @len + * is negative or zero (in which case %NULL will be returned). + * @len: the length of strings in @str. If negative, strv is assumed + * to be a NULL terminated array. + * + * Like g_strdupv(), with two differences: + * + * - accepts a @len parameter for non-null terminated strv array. + * + * - this never returns an empty strv array, but always %NULL if + * there are no strings. + * + * Note that if @len is non-negative, then it still must not + * contain any %NULL pointers within the first @len elements. + * Otherwise you would leak elements if you try to free the + * array with g_strfreev(). Allowing that would be error prone. + * + * Returns: (transfer full): a clone of the strv array. Always + * %NULL terminated. + */ +char ** +nm_utils_strv_dup (gpointer strv, gssize len) +{ + gsize i, l; + char **v; + const char *const *const src = strv; + + if (len < 0) + l = NM_PTRARRAY_LEN (src); + else + l = len; + if (l == 0) { + /* this function never returns an empty strv array. If you + * need that, handle it yourself. */ + return NULL; + } + + v = g_new (char *, l + 1); + for (i = 0; i < l; i++) { + + if (G_UNLIKELY (!src[i])) { + /* NULL strings are not allowed. Clear the remainder of the array + * and return it (with assertion failure). */ + l++; + for (; i < l; i++) + v[i] = NULL; + g_return_val_if_reached (v); + } + + v[i] = g_strdup (src[i]); + } + v[l] = NULL; + return v; +} + /*****************************************************************************/ gssize @@ -2499,8 +2710,8 @@ fail: * @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. + * number, @strv is allowed to contain %NULL strings + * too. * * Ascending sort of the array @strv inplace, using plain strcmp() string * comparison. @@ -2508,9 +2719,16 @@ fail: void _nm_utils_strv_sort (const char **strv, gssize len) { + GCompareDataFunc cmp; gsize l; - l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len; + if (len < 0) { + l = NM_PTRARRAY_LEN (strv); + cmp = nm_strcmp_p_with_data; + } else { + l = len; + cmp = nm_strcmp0_p_with_data; + } if (l <= 1) return; @@ -2520,7 +2738,7 @@ _nm_utils_strv_sort (const char **strv, gssize len) g_qsort_with_data (strv, l, sizeof (const char *), - nm_strcmp_p_with_data, + cmp, NULL); } @@ -2577,6 +2795,53 @@ _nm_utils_strv_cmp_n (const char *const*strv1, /*****************************************************************************/ +/** + * nm_utils_g_slist_find_str: + * @list: the #GSList with NUL terminated strings to search + * @needle: the needle string to look for. + * + * Search the list for @needle and return the first found match + * (or %NULL if not found). Uses strcmp() for finding the first matching + * element. + * + * Returns: the #GSList element with @needle as string value or + * %NULL if not found. + */ +GSList * +nm_utils_g_slist_find_str (const GSList *list, + const char *needle) +{ + nm_assert (needle); + + for (; list; list = list->next) { + nm_assert (list->data); + if (nm_streq (list->data, needle)) + return (GSList *) list; + } + return NULL; +} + +/** + * nm_utils_g_slist_strlist_cmp: + * @a: the left #GSList of strings + * @b: the right #GSList of strings to compare. + * + * Compares two string lists. The data elements are compared with + * strcmp(), alloing %NULL elements. + * + * Returns: 0, 1, or -1, depending on how the lists compare. + */ +int +nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b) +{ + for (; a && b; a = a->next, b = b->next) + NM_CMP_DIRECT_STRCMP0 (a->data, b->data); + NM_CMP_SELF (a, b); + return 0; +} + +/*****************************************************************************/ + gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args) { @@ -2753,10 +3018,13 @@ nm_utils_memeqzero (gconstpointer data, gsize length) * be returned and must be freed by the caller. * If not %NULL, the buffer must already be preallocated and contain * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. + * If @length is zero, then of course at least one byte will be allocated + * or @out (if given) must contain at least room for the trailing NUL byte. * * Returns: the binary value converted to a hex string. If @out is given, * this always returns @out. If @out is %NULL, a newly allocated string - * is returned. + * is returned. This never returns %NULL, for buffers of length zero + * an empty string is returend. */ char * nm_utils_bin2hexstr_full (gconstpointer addr, @@ -2772,9 +3040,11 @@ nm_utils_bin2hexstr_full (gconstpointer addr, if (out) out0 = out; else { - out0 = out = g_new (char, delimiter == '\0' - ? length * 2 + 1 - : length * 3); + out0 = out = g_new (char, length == 0 + ? 1u + : ( delimiter == '\0' + ? length * 2u + 1u + : length * 3u)); } /* @out must contain at least @length*3 bytes if @delimiter is set, @@ -2939,3 +3209,64 @@ fail: NM_SET_OUT (out_len, 0); return NULL; } + +/*****************************************************************************/ + +GVariant * +nm_utils_gvariant_vardict_filter (GVariant *src, + gboolean (*filter_fcn) (const char *key, + GVariant *val, + char **out_key, + GVariant **out_val, + gpointer user_data), + gpointer user_data) +{ + GVariantIter iter; + GVariantBuilder builder; + const char *key; + GVariant *val; + + g_return_val_if_fail (src && g_variant_is_of_type (src, G_VARIANT_TYPE_VARDICT), NULL); + g_return_val_if_fail (filter_fcn, NULL); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_iter_init (&iter, src); + while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) { + _nm_unused gs_unref_variant GVariant *val_free = val; + gs_free char *key2 = NULL; + gs_unref_variant GVariant *val2 = NULL; + + if (filter_fcn (key, + val, + &key2, + &val2, + user_data)) { + g_variant_builder_add (&builder, + "{sv}", + key2 ?: key, + val2 ?: val); + } + } + + return g_variant_builder_end (&builder); +} + +static gboolean +_gvariant_vardict_filter_drop_one (const char *key, + GVariant *val, + char **out_key, + GVariant **out_val, + gpointer user_data) +{ + return !nm_streq (key, user_data); +} + +GVariant * +nm_utils_gvariant_vardict_filter_drop_one (GVariant *src, + const char *key) +{ + return nm_utils_gvariant_vardict_filter (src, + _gvariant_vardict_filter_drop_one, + (gpointer) key); +} diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index af3c2f83..d9c430d4 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or @@ -171,6 +170,13 @@ nm_ip4_addr_is_localhost (in_addr_t addr4) return _cc < 0 ? -1 : 1; \ } G_STMT_END +#define NM_CMP_RETURN_DIRECT(c) \ + G_STMT_START { \ + const int _cc = (c); \ + if (_cc) \ + return _cc; \ + } G_STMT_END + #define NM_CMP_SELF(a, b) \ G_STMT_START { \ typeof (a) _a = (a); \ @@ -196,8 +202,11 @@ nm_ip4_addr_is_localhost (in_addr_t addr4) #define NM_CMP_DIRECT_MEMCMP(a, b, size) \ NM_CMP_RETURN (memcmp ((a), (b), (size))) +#define NM_CMP_DIRECT_STRCMP(a, b) \ + NM_CMP_RETURN_DIRECT (strcmp ((a), (b))) + #define NM_CMP_DIRECT_STRCMP0(a, b) \ - NM_CMP_RETURN (g_strcmp0 ((a), (b))) + NM_CMP_RETURN_DIRECT (nm_strcmp0 ((a), (b))) #define NM_CMP_DIRECT_IN6ADDR(a, b) \ G_STMT_START { \ @@ -229,16 +238,16 @@ nm_ip4_addr_is_localhost (in_addr_t addr4) const char *_b = ((b)->field); \ \ if (_a != _b) { \ - NM_CMP_RETURN (g_strcmp0 (_a, _b)); \ + NM_CMP_RETURN_DIRECT (nm_strcmp0 (_a, _b)); \ } \ } G_STMT_END #define NM_CMP_FIELD_STR0(a, b, field) \ - NM_CMP_RETURN (g_strcmp0 (((a)->field), ((b)->field))) + NM_CMP_RETURN_DIRECT (nm_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)))) + NM_MIN (len, sizeof ((a)->field)))) #define NM_CMP_FIELD_MEMCMP(a, b, field) \ NM_CMP_RETURN (memcmp (&((a)->field), \ @@ -305,6 +314,20 @@ GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes); /*****************************************************************************/ +GVariant *nm_utils_gvariant_vardict_filter (GVariant *src, + gboolean (*filter_fcn) (const char *key, + GVariant *val, + char **out_key, + GVariant **out_val, + gpointer user_data), + gpointer user_data); + +GVariant * +nm_utils_gvariant_vardict_filter_drop_one (GVariant *src, + const char *key); + +/*****************************************************************************/ + static inline int nm_utils_hexchar_to_int (char ch) { @@ -693,6 +716,8 @@ _nm_g_slice_free_fcn_define (16) * @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_SETTING_MISSING: the setting is missing + * * @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument. */ typedef enum { @@ -715,6 +740,8 @@ typedef enum { NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + NM_UTILS_ERROR_SETTING_MISSING, + } NMUtilsError; #define NM_UTILS_ERROR (nm_utils_error_quark ()) @@ -735,7 +762,13 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal) } #define nm_utils_error_set(error, error_code, ...) \ - g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__) + G_STMT_START { \ + if (NM_NARG (__VA_ARGS__) == 1) { \ + g_set_error_literal ((error), NM_UTILS_ERROR, (error_code), _NM_UTILS_MACRO_FIRST (__VA_ARGS__)); \ + } else { \ + g_set_error ((error), NM_UTILS_ERROR, (error_code), __VA_ARGS__); \ + } \ + } G_STMT_END #define nm_utils_error_set_errno(error, errsv, fmt, ...) \ G_STMT_START { \ @@ -885,7 +918,9 @@ nm_utf8_collate0 (const char *a, const char *b) return g_utf8_collate (a, b); } +int nm_strcmp_with_data (gconstpointer a, gconstpointer b, gpointer user_data); int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data); +int nm_strcmp0_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); @@ -906,11 +941,26 @@ typedef struct { }; } 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); +gssize nm_utils_named_value_list_find (const NMUtilsNamedValue *arr, + gsize len, + const char *name, + gboolean sorted); + +gboolean nm_utils_named_value_list_is_sorted (const NMUtilsNamedValue *arr, + gsize len, + gboolean accept_duplicates, + GCompareDataFunc compare_func, + gpointer user_data); + +void nm_utils_named_value_list_sort (NMUtilsNamedValue *arr, + gsize len, + GCompareDataFunc compare_func, + gpointer user_data); + +/*****************************************************************************/ + gpointer *nm_utils_hash_keys_to_array (GHashTable *hash, GCompareDataFunc compare_func, gpointer user_data, @@ -927,14 +977,28 @@ nm_utils_strdict_get_keys (const GHashTable *hash, out_length); } +gboolean nm_utils_hashtable_same_keys (const GHashTable *a, + const GHashTable *b); + char **nm_utils_strv_make_deep_copied (const char **strv); +char **nm_utils_strv_make_deep_copied_n (const char **strv, gsize len); + 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); } +char **nm_utils_strv_dup (gpointer strv, gssize len); + +/*****************************************************************************/ + +GSList *nm_utils_g_slist_find_str (const GSList *list, + const char *needle); + +int nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b); + /*****************************************************************************/ gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list, diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c index ae526c34..7735f29d 100644 --- a/shared/nm-glib-aux/nm-time-utils.c +++ b/shared/nm-glib-aux/nm-time-utils.c @@ -22,6 +22,8 @@ #include "nm-time-utils.h" +#include "nm-logging-fwd.h" + /*****************************************************************************/ typedef struct { @@ -229,15 +231,15 @@ nm_utils_get_monotonic_timestamp_s (void) /** * nm_utils_monotonic_timestamp_as_boottime: * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME. - * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if - * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano - * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc. + * @timestamp_ns_per_tick: How many nanoseconds make one unit of @timestamp? E.g. if + * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; if @timestamp is + * in nanoseconds, pass 1; if @timestamp is in milliseconds, pass %NM_UTILS_NS_PER_SECOND/1000. * * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime(). - * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick. - * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. - * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp - * is always positive. + * The unit is the same as the passed in @timestamp based on @timestamp_ns_per_tick. + * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. + * If @timestamp is non-positive, it returns -1. Note that a (valid) monotonic-timestamp + * is always positive. * * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC. **/ @@ -263,6 +265,8 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_ p = _t_get_global_state (); + nm_assert (p->offset_sec <= 0); + /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick); @@ -271,3 +275,23 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_ return timestamp - offset; } + +gint64 +nm_utils_clock_gettime_ns (clockid_t clockid) +{ + struct timespec tp; + + if (clock_gettime (clockid, &tp) != 0) + return -NM_ERRNO_NATIVE (errno); + return nm_utils_timespec_to_ns (&tp); +} + +gint64 +nm_utils_clock_gettime_ms (clockid_t clockid) +{ + struct timespec tp; + + if (clock_gettime (clockid, &tp) != 0) + return -NM_ERRNO_NATIVE (errno); + return nm_utils_timespec_to_ms (&tp); +} diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h index 7e4f4f25..52d6637d 100644 --- a/shared/nm-glib-aux/nm-time-utils.h +++ b/shared/nm-glib-aux/nm-time-utils.h @@ -21,6 +21,22 @@ #ifndef __NM_TIME_UTILS_H__ #define __NM_TIME_UTILS_H__ +#include <time.h> + +static inline gint64 +nm_utils_timespec_to_ns (const struct timespec *ts) +{ + return (((gint64) ts->tv_sec) * ((gint64) NM_UTILS_NS_PER_SECOND)) + + ((gint64) ts->tv_nsec); +} + +static inline gint64 +nm_utils_timespec_to_ms (const struct timespec *ts) +{ + return (((gint64) ts->tv_sec) * ((gint64) 1000)) + + (((gint64) ts->tv_nsec) / ((gint64) NM_UTILS_NS_PER_SECOND / 1000)); +} + gint64 nm_utils_get_monotonic_timestamp_ns (void); gint64 nm_utils_get_monotonic_timestamp_us (void); gint64 nm_utils_get_monotonic_timestamp_ms (void); @@ -34,12 +50,7 @@ nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ()); } -struct timespec; - -/* this function must be implemented to handle the notification when - * the first monotonic-timestamp is fetched. */ -extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp, - gint64 offset_sec, - gboolean is_boottime); +gint64 nm_utils_clock_gettime_ns (clockid_t clockid); +gint64 nm_utils_clock_gettime_ms (clockid_t clockid); #endif /* __NM_TIME_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-value-type.h b/shared/nm-glib-aux/nm-value-type.h new file mode 100644 index 00000000..b4d6898f --- /dev/null +++ b/shared/nm-glib-aux/nm-value-type.h @@ -0,0 +1,208 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_VALUE_TYPE_H__ +#define __NM_VALUE_TYPE_H__ + +typedef enum { + NM_VALUE_TYPE_UNSPEC = 1, + NM_VALUE_TYPE_BOOL = 2, + NM_VALUE_TYPE_INT32 = 3, + NM_VALUE_TYPE_INT = 4, + NM_VALUE_TYPE_STRING = 5, +} NMValueType; + +/*****************************************************************************/ + +#ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS + +typedef union { + bool v_bool; + gint32 v_int32; + int v_int; + const char *v_string; + + /* for convenience, also let the union contain other pointer types. These are + * for NM_VALUE_TYPE_UNSPEC. */ + gconstpointer *v_ptr; + const GPtrArray *v_ptrarray; + +} NMValueTypUnion; + +/* Set the NMValueTypUnion. You can also assign the member directly. + * The only purpose of this is that it also returns a pointer to the + * union. So, you can do + * + * ptr = NM_VALUE_TYP_UNION_SET (&value_typ_union_storage, v_bool, TRUE); + */ +#define NM_VALUE_TYP_UNION_SET(_arg, _type, _val) \ + ({ \ + NMValueTypUnion *const _arg2 = (_arg); \ + \ + *_arg2 = (NMValueTypUnion) { \ + ._type = (_val), \ + }; \ + _arg2; \ + }) + +typedef struct { + bool has; + NMValueTypUnion val; +} NMValueTypUnioMaybe; + +#define NM_VALUE_TYP_UNIO_MAYBE_SET(_arg, _type, _val) \ + ({ \ + NMValueTypUnioMaybe *const _arg2 = (_arg); \ + \ + *_arg2 = (NMValueTypUnioMaybe) { \ + .has = TRUE, \ + .val._type = (_val), \ + }; \ + _arg2; \ + }) + +/*****************************************************************************/ + +static inline int +nm_value_type_cmp (NMValueType value_type, + gconstpointer p_a, + gconstpointer p_b) +{ + switch (value_type) { + case NM_VALUE_TYPE_BOOL: NM_CMP_DIRECT (*((const bool *) p_a), *((const bool *) p_b)); return 0; + case NM_VALUE_TYPE_INT32: NM_CMP_DIRECT (*((const gint32 *) p_a), *((const gint32 *) p_b)); return 0; + case NM_VALUE_TYPE_INT: NM_CMP_DIRECT (*((const int *) p_a), *((const int *) p_b)); return 0; + case NM_VALUE_TYPE_STRING: return nm_strcmp0 (*((const char *const*) p_a), *((const char *const*) p_b)); + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); + return 0; +} + +static inline gboolean +nm_value_type_equal (NMValueType value_type, + gconstpointer p_a, + gconstpointer p_b) +{ + return nm_value_type_cmp (value_type, p_a, p_b) == 0; +} + +static inline void +nm_value_type_copy (NMValueType value_type, + gpointer dst, + gconstpointer src) +{ + switch (value_type) { + case NM_VALUE_TYPE_BOOL: (*((bool *) dst) = *((const bool *) src)); return; + case NM_VALUE_TYPE_INT32: (*((gint32 *) dst) = *((const gint32 *) src)); return; + case NM_VALUE_TYPE_INT: (*((int *) dst) = *((const int *) src)); return; + case NM_VALUE_TYPE_STRING: + /* self assignment safe! */ + if (*((char **) dst) != *((const char *const*) src)) { + g_free (*((char **) dst)); + *((char **) dst) = g_strdup (*((const char *const*) src)); + } + return; + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); +} + +static inline void +nm_value_type_get_from_variant (NMValueType value_type, + gpointer dst, + GVariant *variant, + gboolean clone) +{ + switch (value_type) { + case NM_VALUE_TYPE_BOOL: *((bool *) dst) = g_variant_get_boolean (variant); return; + case NM_VALUE_TYPE_INT32: *((gint32 *) dst) = g_variant_get_int32 (variant); return; + case NM_VALUE_TYPE_STRING: + if (clone) { + g_free (*((char **) dst)); + *((char **) dst) = g_variant_dup_string (variant, NULL); + } else { + /* we don't clone the string, nor free the previous value. */ + *((const char **) dst) = g_variant_get_string (variant, NULL); + } + return; + + case NM_VALUE_TYPE_INT: + /* "int" also does not have a define variant type, because it's not + * clear how many bits we would need. */ + + /* fall-through */ + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); +} + +static inline GVariant * +nm_value_type_to_variant (NMValueType value_type, + gconstpointer src) +{ + const char *v_string; + + switch (value_type) { + case NM_VALUE_TYPE_BOOL: return g_variant_new_boolean (*((const bool *) src)); + case NM_VALUE_TYPE_INT32: return g_variant_new_int32 (*((const gint32 *) src));; + case NM_VALUE_TYPE_STRING: + v_string = *((const char *const*) src); + return v_string ? g_variant_new_string (v_string) : NULL; + + case NM_VALUE_TYPE_INT: + /* "int" also does not have a define variant type, because it's not + * clear how many bits we would need. */ + + /* fall-through */ + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); + return NULL; +} + +static inline const GVariantType * +nm_value_type_get_variant_type (NMValueType value_type) +{ + switch (value_type) { + case NM_VALUE_TYPE_BOOL: return G_VARIANT_TYPE_BOOLEAN; + case NM_VALUE_TYPE_INT32: return G_VARIANT_TYPE_INT32; + case NM_VALUE_TYPE_STRING: return G_VARIANT_TYPE_STRING; + + case NM_VALUE_TYPE_INT: + /* "int" also does not have a define variant type, because it's not + * clear how many bits we would need. */ + + /* fall-through */ + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); + return NULL; +} + +/*****************************************************************************/ + +#endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ + +#endif /* __NM_VALUE_TYPE_H__ */ |