From c0c10896e95c9ffc76e556a78f6cc883a4096b19 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 9 Jan 2018 08:42:56 +0100 Subject: [PATCH 01/13] libnm: introduce NMJsonVt virtual table for accessing libjansson symbols Rework the code how we access libjansson. libnm wants to use libjansson, but it doesn't directly link to it. The reason is that (until recently), libjansson has conflicting symbols with libjson-c and libjson-glib. That means, if libnm would directly link against libjansson, then if the using application happens to drag in one of the conflicting libraries, the application would crash. Instead, we dlopen() the library (with flags RTLD_LOCAL|RTLD_DEEPBIND). However, as it is currently done, it doesn't fully work, as unit test failures of libnm show on Debian sid (where libmount links against libcryptsetup which links against libjson-c). Theoretically, our current approach should work. At least for libnm; not for the OVS and team plugins which use libjansson directly in NetworkManager core. What I dislike about the current approach is that we still include , but somehow try not to use any symbols from it (via #define we remap the json functions). The previous approach is "smaller", but also highly confusing, and error prone, as there is a subtle bug as the unit test failure shows (which I don't understand). Instead, add and load a virtual function table NMJsonVt. Later, we will go further ad drop all direct uses of header. --- libnm-core/nm-json.c | 234 ++++++++++++++++++++++++------------- libnm-core/nm-json.h | 115 +++++++++++++----- libnm-core/nm-team-utils.c | 2 +- libnm-core/nm-utils.c | 2 +- 4 files changed, 241 insertions(+), 112 deletions(-) diff --git a/libnm-core/nm-json.c b/libnm-core/nm-json.c index 001d53dbb6..f20027a0bd 100644 --- a/libnm-core/nm-json.c +++ b/libnm-core/nm-json.c @@ -5,106 +5,174 @@ #include "nm-default.h" -#define NM_JANSSON_C #include "nm-json.h" #include -void *_nm_jansson_json_object_iter_value; -void *_nm_jansson_json_object_key_to_iter; -void *_nm_jansson_json_integer; -void *_nm_jansson_json_object_del; -void *_nm_jansson_json_array_get; -void *_nm_jansson_json_array_size; -void *_nm_jansson_json_array_append_new; -void *_nm_jansson_json_string; -void *_nm_jansson_json_object_iter_next; -void *_nm_jansson_json_loads; -void *_nm_jansson_json_dumps; -void *_nm_jansson_json_object_iter_key; -void *_nm_jansson_json_object; -void *_nm_jansson_json_object_get; -void *_nm_jansson_json_array; -void *_nm_jansson_json_false; -void *_nm_jansson_json_delete; -void *_nm_jansson_json_true; -void *_nm_jansson_json_object_size; -void *_nm_jansson_json_object_set_new; -void *_nm_jansson_json_object_iter; -void *_nm_jansson_json_integer_value; -void *_nm_jansson_json_string_value; +typedef struct { + NMJsonVt vt; + void *dl_handle; +} NMJsonVtInternal; -#define TRY_BIND_SYMBOL(symbol) \ - G_STMT_START { \ - void *sym = dlsym (handle, #symbol); \ - if (_nm_jansson_ ## symbol && sym != _nm_jansson_ ## symbol) \ - return FALSE; \ - _nm_jansson_ ## symbol = sym; \ - } G_STMT_END - -static gboolean -bind_symbols (void *handle) +static NMJsonVtInternal * +_nm_json_vt_internal_load (void) { - TRY_BIND_SYMBOL (json_object_iter_value); - TRY_BIND_SYMBOL (json_object_key_to_iter); - TRY_BIND_SYMBOL (json_integer); - TRY_BIND_SYMBOL (json_object_del); - TRY_BIND_SYMBOL (json_array_get); - TRY_BIND_SYMBOL (json_array_size); - TRY_BIND_SYMBOL (json_array_append_new); - TRY_BIND_SYMBOL (json_string); - TRY_BIND_SYMBOL (json_object_iter_next); - TRY_BIND_SYMBOL (json_loads); - TRY_BIND_SYMBOL (json_dumps); - TRY_BIND_SYMBOL (json_object_iter_key); - TRY_BIND_SYMBOL (json_object); - TRY_BIND_SYMBOL (json_object_get); - TRY_BIND_SYMBOL (json_array); - TRY_BIND_SYMBOL (json_false); - TRY_BIND_SYMBOL (json_delete); - TRY_BIND_SYMBOL (json_true); - TRY_BIND_SYMBOL (json_object_size); - TRY_BIND_SYMBOL (json_object_set_new); - TRY_BIND_SYMBOL (json_object_iter); - TRY_BIND_SYMBOL (json_integer_value); - TRY_BIND_SYMBOL (json_string_value); - - return TRUE; -} - -gboolean -nm_jansson_load (void) -{ - static enum { - UNKNOWN, - AVAILABLE, - MISSING, - } state = UNKNOWN; - void *handle; + NMJsonVtInternal *v; + void *handle = NULL; int mode; - if (G_LIKELY (state != UNKNOWN)) - goto out; + v = g_new0 (NMJsonVtInternal, 1); - /* First just resolve the symbols to see if there's a conflict already. */ - if (!bind_symbols (RTLD_DEFAULT)) - goto out; +#ifndef JANSSON_SONAME +#define JANSSON_SONAME "" +#endif mode = RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND; #if defined (ASAN_BUILD) /* Address sanitizer is incompatible with RTLD_DEEPBIND. */ mode &= ~RTLD_DEEPBIND; #endif - handle = dlopen (JANSSON_SONAME, mode); + + if (strlen (JANSSON_SONAME) > 0) + handle = dlopen (JANSSON_SONAME, mode); if (!handle) - goto out; + return v; - /* Now do the actual binding. */ - if (!bind_symbols (handle)) - goto out; +#define TRY_BIND_SYMBOL(symbol) \ + G_STMT_START { \ + void *_sym = dlsym (handle, "json" #symbol); \ + \ + if (!_sym) \ + goto fail_symbol; \ + v->vt.nm_json ## symbol = _sym; \ + } G_STMT_END - state = AVAILABLE; -out: - return state == AVAILABLE; + TRY_BIND_SYMBOL (_array); + TRY_BIND_SYMBOL (_array_append_new); + TRY_BIND_SYMBOL (_array_get); + TRY_BIND_SYMBOL (_array_size); + TRY_BIND_SYMBOL (_delete); + TRY_BIND_SYMBOL (_dumps); + TRY_BIND_SYMBOL (_false); + TRY_BIND_SYMBOL (_integer); + TRY_BIND_SYMBOL (_integer_value); + TRY_BIND_SYMBOL (_loads); + TRY_BIND_SYMBOL (_object); + TRY_BIND_SYMBOL (_object_del); + TRY_BIND_SYMBOL (_object_get); + TRY_BIND_SYMBOL (_object_iter); + TRY_BIND_SYMBOL (_object_iter_key); + TRY_BIND_SYMBOL (_object_iter_next); + TRY_BIND_SYMBOL (_object_iter_value); + TRY_BIND_SYMBOL (_object_key_to_iter); + TRY_BIND_SYMBOL (_object_set_new); + TRY_BIND_SYMBOL (_object_size); + TRY_BIND_SYMBOL (_string); + TRY_BIND_SYMBOL (_string_value); + TRY_BIND_SYMBOL (_true); + + v->vt.loaded = TRUE; + v->dl_handle = handle; + return v; + +fail_symbol: + dlclose (&handle); + *v = (NMJsonVtInternal) { }; + return v; } + +const NMJsonVt *_nm_json_vt_ptr = NULL; + +const NMJsonVt * +_nm_json_vt_init (void) +{ + NMJsonVtInternal *v; + +again: + v = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); + if (G_UNLIKELY (!v)) { + v = _nm_json_vt_internal_load (); + if (!g_atomic_pointer_compare_and_exchange ((gpointer *) &_nm_json_vt_ptr, NULL, v)) { + if (v->dl_handle) + dlclose (v->dl_handle); + g_free (v); + goto again; + } + + /* we transfer ownership. */ + } + + nm_assert (v && v == g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr)); + return &v->vt; +} + +const NMJsonVt * +nmtst_json_vt_reset (gboolean loaded) +{ + NMJsonVtInternal *v_old; + NMJsonVtInternal *v; + + v_old = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); + + if (!loaded) { + /* load a fake instance for testing. */ + v = g_new0 (NMJsonVtInternal, 1); + } else + v = _nm_json_vt_internal_load (); + + if (!g_atomic_pointer_compare_and_exchange ((gpointer *) &_nm_json_vt_ptr, v_old, v)) + g_assert_not_reached (); + + if (v_old) { + if (v_old->dl_handle) + dlclose (v_old->dl_handle); + g_free ((gpointer *) v_old); + } + + return v->vt.loaded ? &v->vt : NULL; +} + +#define DEF_FCN(name, rval, args_t, args_v) \ +rval name args_t \ +{ \ + const NMJsonVt *vt = nm_json_vt (); \ + \ + nm_assert (vt && vt->loaded && vt->name); \ + nm_assert (vt->name != name); \ + return (vt->name) args_v; \ +} + +#define DEF_VOI(name, args_t, args_v) \ +void name args_t \ +{ \ + const NMJsonVt *vt = nm_json_vt (); \ + \ + nm_assert (vt && vt->loaded && vt->name); \ + nm_assert (vt->name != name); \ + (vt->name) args_v; \ +} + +DEF_FCN (nm_json_array, json_t *, (void), ()); +DEF_FCN (nm_json_array_append_new, int, (json_t *json, json_t *value), (json, value)); +DEF_FCN (nm_json_array_get, json_t *, (const json_t *json, size_t index), (json, index)); +DEF_FCN (nm_json_array_size, size_t, (const json_t *json), (json)); +DEF_VOI (nm_json_delete, (json_t *json), (json)); +DEF_FCN (nm_json_dumps, char *, (const json_t *json, size_t flags), (json, flags)); +DEF_FCN (nm_json_false, json_t *, (void), ()); +DEF_FCN (nm_json_integer, json_t *, (json_int_t value), (value)); +DEF_FCN (nm_json_integer_value, json_int_t, (const json_t *json), (json)); +DEF_FCN (nm_json_loads, json_t *, (const char *string, size_t flags, json_error_t *error), (string, flags, error)); +DEF_FCN (nm_json_object, json_t *, (void), ()); +DEF_FCN (nm_json_object_del, int, (json_t *json, const char *key), (json, key)); +DEF_FCN (nm_json_object_get, json_t *, (const json_t *json, const char *key), (json, key)); +DEF_FCN (nm_json_object_iter, void *, (json_t *json), (json)); +DEF_FCN (nm_json_object_iter_key, const char *, (void *iter), (iter)); +DEF_FCN (nm_json_object_iter_next, void *, (json_t *json, void *iter), (json, iter)); +DEF_FCN (nm_json_object_iter_value, json_t *, (void *iter), (iter)); +DEF_FCN (nm_json_object_key_to_iter, void *, (const char *key), (key)); +DEF_FCN (nm_json_object_set_new, int, (json_t *json, const char *key, json_t *value), (json, key, value)); +DEF_FCN (nm_json_object_size, size_t, (const json_t *json), (json)); +DEF_FCN (nm_json_string, json_t *, (const char *value), (value)); +DEF_FCN (nm_json_string_value, const char *, (const json_t *json), (json)); +DEF_FCN (nm_json_true, json_t *, (void), ()); diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h index d857fe0275..2d0f10773d 100644 --- a/libnm-core/nm-json.h +++ b/libnm-core/nm-json.h @@ -6,34 +6,95 @@ #ifndef __NM_JSON_H__ #define __NM_JSON_H__ -gboolean nm_jansson_load (void); - -#ifndef NM_JANSSON_C -#define json_object_iter_value (*_nm_jansson_json_object_iter_value) -#define json_object_key_to_iter (*_nm_jansson_json_object_key_to_iter) -#define json_integer (*_nm_jansson_json_integer) -#define json_object_del (*_nm_jansson_json_object_del) -#define json_array_get (*_nm_jansson_json_array_get) -#define json_array_size (*_nm_jansson_json_array_size) -#define json_array_append_new (*_nm_jansson_json_array_append_new) -#define json_string (*_nm_jansson_json_string) -#define json_object_iter_next (*_nm_jansson_json_object_iter_next) -#define json_loads (*_nm_jansson_json_loads) -#define json_dumps (*_nm_jansson_json_dumps) -#define json_object_iter_key (*_nm_jansson_json_object_iter_key) -#define json_object (*_nm_jansson_json_object) -#define json_object_get (*_nm_jansson_json_object_get) -#define json_array (*_nm_jansson_json_array) -#define json_false (*_nm_jansson_json_false) -#define json_delete (*_nm_jansson_json_delete) -#define json_true (*_nm_jansson_json_true) -#define json_object_size (*_nm_jansson_json_object_size) -#define json_object_set_new (*_nm_jansson_json_object_set_new) -#define json_object_iter (*_nm_jansson_json_object_iter) -#define json_integer_value (*_nm_jansson_json_integer_value) -#define json_string_value (*_nm_jansson_json_string_value) +#define json_array nm_json_array +#define json_array_append_new nm_json_array_append_new +#define json_array_get nm_json_array_get +#define json_array_size nm_json_array_size +#define json_delete nm_json_delete +#define json_dumps nm_json_dumps +#define json_false nm_json_false +#define json_integer nm_json_integer +#define json_integer_value nm_json_integer_value +#define json_loads nm_json_loads +#define json_object nm_json_object +#define json_object_del nm_json_object_del +#define json_object_get nm_json_object_get +#define json_object_iter nm_json_object_iter +#define json_object_iter_key nm_json_object_iter_key +#define json_object_iter_next nm_json_object_iter_next +#define json_object_iter_value nm_json_object_iter_value +#define json_object_key_to_iter nm_json_object_key_to_iter +#define json_object_set_new nm_json_object_set_new +#define json_object_size nm_json_object_size +#define json_string nm_json_string +#define json_string_value nm_json_string_value +#define json_true nm_json_true #include "nm-glib-aux/nm-jansson.h" -#endif + +typedef struct { + gboolean loaded; + char *(*nm_json_dumps) (const json_t *json, size_t flags); + const char *(*nm_json_object_iter_key) (void *iter); + const char *(*nm_json_string_value) (const json_t *json); + int (*nm_json_array_append_new) (json_t *json, json_t *value); + int (*nm_json_object_del) (json_t *json, const char *key); + int (*nm_json_object_set_new) (json_t *json, const char *key, json_t *value); + json_int_t (*nm_json_integer_value) (const json_t *json); + json_t *(*nm_json_array) (void); + json_t *(*nm_json_array_get) (const json_t *json, size_t index); + json_t *(*nm_json_false) (void); + json_t *(*nm_json_integer) (json_int_t value); + json_t *(*nm_json_loads) (const char *string, size_t flags, json_error_t *error); + json_t *(*nm_json_object) (void); + json_t *(*nm_json_object_get) (const json_t *json, const char *key); + json_t *(*nm_json_object_iter_value) (void *); + json_t *(*nm_json_string) (const char *value); + json_t *(*nm_json_true) (void); + size_t (*nm_json_array_size) (const json_t *json); + size_t (*nm_json_object_size) (const json_t *json); + void (*nm_json_delete) (json_t *json); + void *(*nm_json_object_iter) (json_t *json); + void *(*nm_json_object_iter_next) (json_t *json, void *iter); + void *(*nm_json_object_key_to_iter) (const char *key); +} NMJsonVt; + +extern const NMJsonVt *_nm_json_vt_ptr; + +const NMJsonVt *_nm_json_vt_init (void); + +static inline const NMJsonVt * +_nm_json_vt (void) +{ + const NMJsonVt *vt; + + vt = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); + if (G_UNLIKELY (!vt)) { + vt = _nm_json_vt_init (); + nm_assert (vt); + } + return vt; +} + +static inline const NMJsonVt * +nm_json_vt (void) +{ + const NMJsonVt *vt; + + vt = _nm_json_vt(); + return vt->loaded ? vt : NULL; +} + +static inline const NMJsonVt * +nm_json_vt_assert (void) +{ + const NMJsonVt *vt; + + vt = _nm_json_vt(); + nm_assert (vt->loaded); + return vt; +} + +const NMJsonVt *nmtst_json_vt_reset (gboolean loaded); #endif /* __NM_JSON_H__ */ diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index 084547c8f8..af96b995d7 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -1870,7 +1870,7 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) { nm_auto_decref_json json_t *root_js_obj = NULL; - if (nm_jansson_load ()) + if (nm_json_vt ()) root_js_obj = json_loads (js_str, 0, NULL); if ( !root_js_obj diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 4127abb54d..b7b54948b4 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -5507,7 +5507,7 @@ nm_utils_is_json_object (const char *str, GError **error) return FALSE; } - if (!nm_jansson_load ()) + if (!nm_json_vt ()) return _nm_utils_is_json_object_no_validation (str, error); json = json_loads (str, JSON_REJECT_DUPLICATES, &jerror); From 125d362177eeabf49d04216bb62ae4c31c55e94c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 9 Jan 2018 09:15:04 +0100 Subject: [PATCH 02/13] libnm: don't use any symbols from jansson.h directly Some symbols in jansson.h are macros, some are regular functions, and some are inline functions. Regular functions must not be used directly, only via dlsym(). Macros must be used directly, but it is non-obvious which symbols are macros. Hence, for each json_* macro add an nm_json_* alias. Inline functions are a bit odd. If they are inlined and don't use any non-inlined symbols from libjansson, they could be used directly. However, it's non obvious whether both of the conditions are met. Hence, we reimplement them in nm-json.h. The only function of this kind is json_decref(). The point is to not use any json_* symbols directly -- except structs and typedefs. Seemingly, with this change we don't use any jansson symbols directly. However, that is not true, as macros like nm_json_object_foreach() still are implemented based on what is included from . Hence, we cannot drop patching the included jansson.h header yet and still need our wrapper functions. --- libnm-core/nm-json.h | 32 ++++++++++++++++++++++++++++++++ libnm-core/nm-team-utils.c | 28 ++++++++++++++-------------- libnm-core/nm-utils.c | 7 ++++--- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h index 2d0f10773d..3f99609e37 100644 --- a/libnm-core/nm-json.h +++ b/libnm-core/nm-json.h @@ -97,4 +97,36 @@ nm_json_vt_assert (void) const NMJsonVt *nmtst_json_vt_reset (gboolean loaded); +#define nm_json_boolean(vt, val) \ + ((val) ? (vt)->nm_json_true () : (vt)->nm_json_false ()) + +static inline void +nm_json_decref (const NMJsonVt *vt, json_t *json) +{ + /* Our ref-counting is not threadsafe, unlike libjansson's. But we never + * share one json_t instance between threads, and if we would, we would very likely + * wrap a mutex around it. */ + if ( json + && json->refcount != (size_t) -1 + && --json->refcount == 0) + vt->nm_json_delete (json); +} + +/*****************************************************************************/ + +/* the following are implemented as pure macros in jansson.h. + * They can be used directly, however, add a nm_json* variant, + * to make it explict we don't accidentally use jansson ABI. */ + +#define nm_json_is_boolean(json) json_is_boolean (json) +#define nm_json_is_integer(json) json_is_integer (json) +#define nm_json_is_string(json) json_is_string (json) +#define nm_json_is_object(json) json_is_object (json) +#define nm_json_is_array(json) json_is_array (json) +#define nm_json_is_true(json) json_is_true (json) +#define nm_json_boolean_value(json) json_boolean_value (json) +#define nm_json_array_foreach(a, b, c) json_array_foreach (a, b, c) +#define nm_json_object_foreach(a, b, c) json_object_foreach (a, b, c) +#define nm_json_object_foreach_safe(a, b, c, d) json_object_foreach_safe (a, b, c, d) + #endif /* __NM_JSON_H__ */ diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index af96b995d7..f08d50d26a 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -1127,10 +1127,10 @@ _link_watcher_from_json (const json_t *root_js_obj, const char *v_name; NMTeamLinkWatcher *result = NULL; - if (!json_is_object (root_js_obj)) + if (!nm_json_is_object (root_js_obj)) goto fail; - json_object_foreach ((json_t *) root_js_obj, j_key, j_val) { + nm_json_object_foreach ((json_t *) root_js_obj, j_key, j_val) { const LinkWatcherAttrData *attr_data = NULL; NMValueTypUnioMaybe *parse_result; @@ -1713,18 +1713,18 @@ _js_parse_locate_keys (NMTeamSetting *self, (_found_keys)[_attr_data->team_attr] = (_cur_val); \ _handled = TRUE; \ } else if ( !_attr_data \ - || !json_is_object ((_cur_val))) { \ + || !nm_json_is_object ((_cur_val))) { \ *(_out_unrecognized_content) = TRUE; \ _handled = TRUE; \ } \ _handled; \ }) - json_object_foreach (root_js_obj, cur_key1, cur_val1) { + nm_json_object_foreach (root_js_obj, cur_key1, cur_val1) { if (!_handle (self, cur_key1, cur_val1, keys, 1, found_keys, out_unrecognized_content)) { - json_object_foreach (cur_val1, cur_key2, cur_val2) { + nm_json_object_foreach (cur_val1, cur_key2, cur_val2) { if (!_handle (self, cur_key2, cur_val2, keys, 2, found_keys, out_unrecognized_content)) { - json_object_foreach (cur_val2, cur_key3, cur_val3) { + nm_json_object_foreach (cur_val2, cur_key3, cur_val3) { if (!_handle (self, cur_key3, cur_val3, keys, 3, found_keys, out_unrecognized_content)) *out_unrecognized_content = TRUE; } @@ -1770,13 +1770,13 @@ _js_parse_unpack (gboolean is_port, NMTeamLinkWatcher *link_watcher; nm_assert (out_ptr_array_link_watchers_free && !*out_ptr_array_link_watchers_free); - if (json_is_array (arg_js_obj)) { + if (nm_json_is_array (arg_js_obj)) { gsize i, len; - len = json_array_size (arg_js_obj); + len = nm_json_array_size (arg_js_obj); link_watchers = g_ptr_array_new_full (len, (GDestroyNotify) nm_team_link_watcher_unref); for (i = 0; i < len; i++) { - link_watcher = _link_watcher_from_json (json_array_get (arg_js_obj, i), + link_watcher = _link_watcher_from_json (nm_json_array_get (arg_js_obj, i), out_unrecognized_content); if (link_watcher) g_ptr_array_add (link_watchers, link_watcher); @@ -1799,16 +1799,16 @@ _js_parse_unpack (gboolean is_port, GPtrArray *strv = NULL; nm_assert (out_ptr_array_master_runner_tx_hash_free && !*out_ptr_array_master_runner_tx_hash_free); - if (json_is_array (arg_js_obj)) { + if (nm_json_is_array (arg_js_obj)) { gsize i, len; - len = json_array_size (arg_js_obj); + len = nm_json_array_size (arg_js_obj); if (len > 0) { strv = g_ptr_array_sized_new (len); for (i = 0; i < len; i++) { const char *v_string; - if ( nm_jansson_json_as_string (json_array_get (arg_js_obj, i), + if ( nm_jansson_json_as_string (nm_json_array_get (arg_js_obj, i), &v_string) <= 0 || !v_string || v_string[0] == '\0') { @@ -1871,10 +1871,10 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) nm_auto_decref_json json_t *root_js_obj = NULL; if (nm_json_vt ()) - root_js_obj = json_loads (js_str, 0, NULL); + root_js_obj = nm_json_loads (js_str, 0, NULL); if ( !root_js_obj - || !json_is_object (root_js_obj)) + || !nm_json_is_object (root_js_obj)) new_js_str_invalid = TRUE; else { gboolean unrecognized_content = FALSE; diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index b7b54948b4..8b6b121332 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -5495,6 +5495,7 @@ nm_utils_is_json_object (const char *str, GError **error) { #if WITH_JSON_VALIDATION nm_auto_decref_json json_t *json = NULL; + const NMJsonVt *vt; json_error_t jerror; g_return_val_if_fail (!error || !*error, FALSE); @@ -5507,10 +5508,10 @@ nm_utils_is_json_object (const char *str, GError **error) return FALSE; } - if (!nm_json_vt ()) + if (!(vt = nm_json_vt ())) return _nm_utils_is_json_object_no_validation (str, error); - json = json_loads (str, JSON_REJECT_DUPLICATES, &jerror); + json = vt->nm_json_loads (str, JSON_REJECT_DUPLICATES, &jerror); if (!json) { g_set_error (error, NM_CONNECTION_ERROR, @@ -5523,7 +5524,7 @@ nm_utils_is_json_object (const char *str, GError **error) /* valid JSON (depending on the definition) can also be a literal. * Here we only allow objects. */ - if (!json_is_object (json)) { + if (!nm_json_is_object (json)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, From a9e3987e7cf189d81540e0db675c79bee1c6ca0f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 16:44:55 +0200 Subject: [PATCH 03/13] libnm: merge "shared/nm-glib-aux/nm-jansson.h" into "libnm-core/nm-json.h" "shared/nm-glib-aux/nm-jansson.h" is a compat header for . It includes and adds some compatibility workarounds and helper functions. We want that "libnm-core/nm-json.h" no longer includes , so that we don't accidentally use symbols from there. Hence, "libnm-core/nm-json.h" must no longer include "nm-jansson.h". In preparation of that, copy the content of "shared/nm-glib-aux/nm-jansson.h" also to "libnm-core/nm-json.h". It will be reworked later. --- libnm-core/nm-json.h | 131 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h index 3f99609e37..e1b4786eba 100644 --- a/libnm-core/nm-json.h +++ b/libnm-core/nm-json.h @@ -30,7 +30,11 @@ #define json_string_value nm_json_string_value #define json_true nm_json_true -#include "nm-glib-aux/nm-jansson.h" +/*****************************************************************************/ + +#if WITH_JANSSON + +#include typedef struct { gboolean loaded; @@ -97,6 +101,8 @@ nm_json_vt_assert (void) const NMJsonVt *nmtst_json_vt_reset (gboolean loaded); +/*****************************************************************************/ + #define nm_json_boolean(vt, val) \ ((val) ? (vt)->nm_json_true () : (vt)->nm_json_false ()) @@ -129,4 +135,127 @@ nm_json_decref (const NMJsonVt *vt, json_t *json) #define nm_json_object_foreach(a, b, c) json_object_foreach (a, b, c) #define nm_json_object_foreach_safe(a, b, c, d) json_object_foreach_safe (a, b, c, d) +/*****************************************************************************/ + +/* Added in Jansson v2.7 */ +#ifndef json_boolean_value +#define json_boolean_value json_is_true +#endif + +/* Added in Jansson v2.8 */ +#ifndef json_object_foreach_safe +#define json_object_foreach_safe(object, n, key, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) +#endif + +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-glib-aux/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 /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ + +#endif /* WITH_JANSSON */ + #endif /* __NM_JSON_H__ */ From 185dca22bc5186a00e0bec13be0c3a74f8eeceb3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 9 Jan 2018 10:33:59 +0100 Subject: [PATCH 04/13] libnm: don't redefine symbols in jansson.h header Redefining symbols before including is confusing. Instead, only use our symbols and access libjansson via NMJsonVt. --- libnm-core/nm-json.c | 94 ++++++++++---------------------------- libnm-core/nm-json.h | 74 +++++++++++++----------------- libnm-core/nm-team-utils.c | 45 ++++++++++-------- 3 files changed, 84 insertions(+), 129 deletions(-) diff --git a/libnm-core/nm-json.c b/libnm-core/nm-json.c index f20027a0bd..d6dbc0238b 100644 --- a/libnm-core/nm-json.c +++ b/libnm-core/nm-json.c @@ -41,36 +41,36 @@ _nm_json_vt_internal_load (void) #define TRY_BIND_SYMBOL(symbol) \ G_STMT_START { \ - void *_sym = dlsym (handle, "json" #symbol); \ + typeof (symbol) (*_sym) = dlsym (handle, #symbol); \ \ if (!_sym) \ goto fail_symbol; \ - v->vt.nm_json ## symbol = _sym; \ + v->vt.nm_ ## symbol = _sym; \ } G_STMT_END - TRY_BIND_SYMBOL (_array); - TRY_BIND_SYMBOL (_array_append_new); - TRY_BIND_SYMBOL (_array_get); - TRY_BIND_SYMBOL (_array_size); - TRY_BIND_SYMBOL (_delete); - TRY_BIND_SYMBOL (_dumps); - TRY_BIND_SYMBOL (_false); - TRY_BIND_SYMBOL (_integer); - TRY_BIND_SYMBOL (_integer_value); - TRY_BIND_SYMBOL (_loads); - TRY_BIND_SYMBOL (_object); - TRY_BIND_SYMBOL (_object_del); - TRY_BIND_SYMBOL (_object_get); - TRY_BIND_SYMBOL (_object_iter); - TRY_BIND_SYMBOL (_object_iter_key); - TRY_BIND_SYMBOL (_object_iter_next); - TRY_BIND_SYMBOL (_object_iter_value); - TRY_BIND_SYMBOL (_object_key_to_iter); - TRY_BIND_SYMBOL (_object_set_new); - TRY_BIND_SYMBOL (_object_size); - TRY_BIND_SYMBOL (_string); - TRY_BIND_SYMBOL (_string_value); - TRY_BIND_SYMBOL (_true); + TRY_BIND_SYMBOL (json_array); + TRY_BIND_SYMBOL (json_array_append_new); + TRY_BIND_SYMBOL (json_array_get); + TRY_BIND_SYMBOL (json_array_size); + TRY_BIND_SYMBOL (json_delete); + TRY_BIND_SYMBOL (json_dumps); + TRY_BIND_SYMBOL (json_false); + TRY_BIND_SYMBOL (json_integer); + TRY_BIND_SYMBOL (json_integer_value); + TRY_BIND_SYMBOL (json_loads); + TRY_BIND_SYMBOL (json_object); + TRY_BIND_SYMBOL (json_object_del); + TRY_BIND_SYMBOL (json_object_get); + TRY_BIND_SYMBOL (json_object_iter); + TRY_BIND_SYMBOL (json_object_iter_key); + TRY_BIND_SYMBOL (json_object_iter_next); + TRY_BIND_SYMBOL (json_object_iter_value); + TRY_BIND_SYMBOL (json_object_key_to_iter); + TRY_BIND_SYMBOL (json_object_set_new); + TRY_BIND_SYMBOL (json_object_size); + TRY_BIND_SYMBOL (json_string); + TRY_BIND_SYMBOL (json_string_value); + TRY_BIND_SYMBOL (json_true); v->vt.loaded = TRUE; v->dl_handle = handle; @@ -132,47 +132,3 @@ nmtst_json_vt_reset (gboolean loaded) return v->vt.loaded ? &v->vt : NULL; } - -#define DEF_FCN(name, rval, args_t, args_v) \ -rval name args_t \ -{ \ - const NMJsonVt *vt = nm_json_vt (); \ - \ - nm_assert (vt && vt->loaded && vt->name); \ - nm_assert (vt->name != name); \ - return (vt->name) args_v; \ -} - -#define DEF_VOI(name, args_t, args_v) \ -void name args_t \ -{ \ - const NMJsonVt *vt = nm_json_vt (); \ - \ - nm_assert (vt && vt->loaded && vt->name); \ - nm_assert (vt->name != name); \ - (vt->name) args_v; \ -} - -DEF_FCN (nm_json_array, json_t *, (void), ()); -DEF_FCN (nm_json_array_append_new, int, (json_t *json, json_t *value), (json, value)); -DEF_FCN (nm_json_array_get, json_t *, (const json_t *json, size_t index), (json, index)); -DEF_FCN (nm_json_array_size, size_t, (const json_t *json), (json)); -DEF_VOI (nm_json_delete, (json_t *json), (json)); -DEF_FCN (nm_json_dumps, char *, (const json_t *json, size_t flags), (json, flags)); -DEF_FCN (nm_json_false, json_t *, (void), ()); -DEF_FCN (nm_json_integer, json_t *, (json_int_t value), (value)); -DEF_FCN (nm_json_integer_value, json_int_t, (const json_t *json), (json)); -DEF_FCN (nm_json_loads, json_t *, (const char *string, size_t flags, json_error_t *error), (string, flags, error)); -DEF_FCN (nm_json_object, json_t *, (void), ()); -DEF_FCN (nm_json_object_del, int, (json_t *json, const char *key), (json, key)); -DEF_FCN (nm_json_object_get, json_t *, (const json_t *json, const char *key), (json, key)); -DEF_FCN (nm_json_object_iter, void *, (json_t *json), (json)); -DEF_FCN (nm_json_object_iter_key, const char *, (void *iter), (iter)); -DEF_FCN (nm_json_object_iter_next, void *, (json_t *json, void *iter), (json, iter)); -DEF_FCN (nm_json_object_iter_value, json_t *, (void *iter), (iter)); -DEF_FCN (nm_json_object_key_to_iter, void *, (const char *key), (key)); -DEF_FCN (nm_json_object_set_new, int, (json_t *json, const char *key, json_t *value), (json, key, value)); -DEF_FCN (nm_json_object_size, size_t, (const json_t *json), (json)); -DEF_FCN (nm_json_string, json_t *, (const char *value), (value)); -DEF_FCN (nm_json_string_value, const char *, (const json_t *json), (json)); -DEF_FCN (nm_json_true, json_t *, (void), ()); diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h index e1b4786eba..a585d521c4 100644 --- a/libnm-core/nm-json.h +++ b/libnm-core/nm-json.h @@ -6,30 +6,6 @@ #ifndef __NM_JSON_H__ #define __NM_JSON_H__ -#define json_array nm_json_array -#define json_array_append_new nm_json_array_append_new -#define json_array_get nm_json_array_get -#define json_array_size nm_json_array_size -#define json_delete nm_json_delete -#define json_dumps nm_json_dumps -#define json_false nm_json_false -#define json_integer nm_json_integer -#define json_integer_value nm_json_integer_value -#define json_loads nm_json_loads -#define json_object nm_json_object -#define json_object_del nm_json_object_del -#define json_object_get nm_json_object_get -#define json_object_iter nm_json_object_iter -#define json_object_iter_key nm_json_object_iter_key -#define json_object_iter_next nm_json_object_iter_next -#define json_object_iter_value nm_json_object_iter_value -#define json_object_key_to_iter nm_json_object_key_to_iter -#define json_object_set_new nm_json_object_set_new -#define json_object_size nm_json_object_size -#define json_string nm_json_string -#define json_string_value nm_json_string_value -#define json_true nm_json_true - /*****************************************************************************/ #if WITH_JANSSON @@ -118,6 +94,17 @@ nm_json_decref (const NMJsonVt *vt, json_t *json) vt->nm_json_delete (json); } +static inline void +_nm_auto_decref_json (json_t **p_json) +{ + if ( *p_json + && (*p_json)->refcount != (size_t) -1 + && --(*p_json)->refcount == 0) + nm_json_vt ()->nm_json_delete (*p_json); +} + +#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) + /*****************************************************************************/ /* the following are implemented as pure macros in jansson.h. @@ -131,9 +118,11 @@ nm_json_decref (const NMJsonVt *vt, json_t *json) #define nm_json_is_array(json) json_is_array (json) #define nm_json_is_true(json) json_is_true (json) #define nm_json_boolean_value(json) json_boolean_value (json) -#define nm_json_array_foreach(a, b, c) json_array_foreach (a, b, c) -#define nm_json_object_foreach(a, b, c) json_object_foreach (a, b, c) -#define nm_json_object_foreach_safe(a, b, c, d) json_object_foreach_safe (a, b, c, d) + +#define nm_json_object_foreach(vt, object, key, value) \ + for(key = vt->nm_json_object_iter_key (vt->nm_json_object_iter (object)); \ + key && (value = vt->nm_json_object_iter_value (vt->nm_json_object_key_to_iter (key))); \ + key = vt->nm_json_object_iter_key (vt->nm_json_object_iter_next (object, vt->nm_json_object_key_to_iter (key)))) /*****************************************************************************/ @@ -152,9 +141,6 @@ nm_json_decref (const NMJsonVt *vt, json_t *json) n = json_object_iter_next(object, json_object_key_to_iter(key))) #endif -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 @@ -172,7 +158,8 @@ nm_jansson_json_as_bool (const json_t *elem, } static inline int -nm_jansson_json_as_int32 (const json_t *elem, +nm_jansson_json_as_int32 (const NMJsonVt *vt, + const json_t *elem, gint32 *out_val) { json_int_t v; @@ -180,10 +167,10 @@ nm_jansson_json_as_int32 (const json_t *elem, if (!elem) return 0; - if (!json_is_integer (elem)) + if (!nm_json_is_integer (elem)) return -EINVAL; - v = json_integer_value (elem); + v = vt->nm_json_integer_value (elem); if ( v < (gint64) G_MININT32 || v > (gint64) G_MAXINT32) return -ERANGE; @@ -193,7 +180,8 @@ nm_jansson_json_as_int32 (const json_t *elem, } static inline int -nm_jansson_json_as_int (const json_t *elem, +nm_jansson_json_as_int (const NMJsonVt *vt, + const json_t *elem, int *out_val) { json_int_t v; @@ -201,10 +189,10 @@ nm_jansson_json_as_int (const json_t *elem, if (!elem) return 0; - if (!json_is_integer (elem)) + if (!nm_json_is_integer (elem)) return -EINVAL; - v = json_integer_value (elem); + v = vt->nm_json_integer_value (elem); if ( v < (gint64) G_MININT || v > (gint64) G_MAXINT) return -ERANGE; @@ -214,7 +202,8 @@ nm_jansson_json_as_int (const json_t *elem, } static inline int -nm_jansson_json_as_string (const json_t *elem, +nm_jansson_json_as_string (const NMJsonVt *vt, + const json_t *elem, const char **out_val) { if (!elem) @@ -223,7 +212,7 @@ nm_jansson_json_as_string (const json_t *elem, if (!json_is_string (elem)) return -EINVAL; - NM_SET_OUT (out_val, json_string_value (elem)); + NM_SET_OUT (out_val, vt->nm_json_string_value (elem)); return 1; } @@ -234,18 +223,19 @@ nm_jansson_json_as_string (const json_t *elem, #include "nm-glib-aux/nm-value-type.h" static inline gboolean -nm_value_type_from_json (NMValueType value_type, +nm_value_type_from_json (const NMJsonVt *vt, + 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); + case NM_VALUE_TYPE_INT32: return (nm_jansson_json_as_int32 (vt, elem, out_val) > 0); + case NM_VALUE_TYPE_INT: return (nm_jansson_json_as_int (vt, 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_STRING: return (nm_jansson_json_as_string (vt, elem, out_val) > 0); case NM_VALUE_TYPE_UNSPEC: break; diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index f08d50d26a..43dacaaf71 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -1118,7 +1118,8 @@ _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher, #if WITH_JSON_VALIDATION static NMTeamLinkWatcher * -_link_watcher_from_json (const json_t *root_js_obj, +_link_watcher_from_json (const NMJsonVt *vt, + const json_t *root_js_obj, gboolean *out_unrecognized_content) { NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)] = { }; @@ -1130,7 +1131,7 @@ _link_watcher_from_json (const json_t *root_js_obj, if (!nm_json_is_object (root_js_obj)) goto fail; - nm_json_object_foreach ((json_t *) root_js_obj, j_key, j_val) { + nm_json_object_foreach (vt, (json_t *) root_js_obj, j_key, j_val) { const LinkWatcherAttrData *attr_data = NULL; NMValueTypUnioMaybe *parse_result; @@ -1154,7 +1155,7 @@ _link_watcher_from_json (const json_t *root_js_obj, if (parse_result->has) *out_unrecognized_content = TRUE; - if (!nm_value_type_from_json (attr_data->value_type, j_val, &parse_result->val)) + if (!nm_value_type_from_json (vt, attr_data->value_type, j_val, &parse_result->val)) *out_unrecognized_content = TRUE; else parse_result->has = TRUE; @@ -1686,7 +1687,8 @@ _attr_data_find_by_json_key (gboolean is_port, } static void -_js_parse_locate_keys (NMTeamSetting *self, +_js_parse_locate_keys (const NMJsonVt *vt, + NMTeamSetting *self, json_t *root_js_obj, json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], gboolean *out_unrecognized_content) @@ -1720,11 +1722,11 @@ _js_parse_locate_keys (NMTeamSetting *self, _handled; \ }) - nm_json_object_foreach (root_js_obj, cur_key1, cur_val1) { + nm_json_object_foreach (vt, root_js_obj, cur_key1, cur_val1) { if (!_handle (self, cur_key1, cur_val1, keys, 1, found_keys, out_unrecognized_content)) { - nm_json_object_foreach (cur_val1, cur_key2, cur_val2) { + nm_json_object_foreach (vt, cur_val1, cur_key2, cur_val2) { if (!_handle (self, cur_key2, cur_val2, keys, 2, found_keys, out_unrecognized_content)) { - nm_json_object_foreach (cur_val2, cur_key3, cur_val3) { + nm_json_object_foreach (vt, cur_val2, cur_key3, cur_val3) { if (!_handle (self, cur_key3, cur_val3, keys, 3, found_keys, out_unrecognized_content)) *out_unrecognized_content = TRUE; } @@ -1737,7 +1739,8 @@ _js_parse_locate_keys (NMTeamSetting *self, } static void -_js_parse_unpack (gboolean is_port, +_js_parse_unpack (const NMJsonVt *vt, + gboolean is_port, json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], bool out_has_lst[static _NM_TEAM_ATTRIBUTE_NUM], NMValueTypUnion out_val_lst[static _NM_TEAM_ATTRIBUTE_NUM], @@ -1764,7 +1767,7 @@ _js_parse_unpack (gboolean is_port, p_out_val = &out_val_lst[attr_data->team_attr]; if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) - valid = nm_value_type_from_json (attr_data->value_type, arg_js_obj, p_out_val); + valid = nm_value_type_from_json (vt, attr_data->value_type, arg_js_obj, p_out_val); else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) { GPtrArray *link_watchers = NULL; NMTeamLinkWatcher *link_watcher; @@ -1773,16 +1776,18 @@ _js_parse_unpack (gboolean is_port, if (nm_json_is_array (arg_js_obj)) { gsize i, len; - len = nm_json_array_size (arg_js_obj); + len = vt->nm_json_array_size (arg_js_obj); link_watchers = g_ptr_array_new_full (len, (GDestroyNotify) nm_team_link_watcher_unref); for (i = 0; i < len; i++) { - link_watcher = _link_watcher_from_json (nm_json_array_get (arg_js_obj, i), + link_watcher = _link_watcher_from_json (vt, + vt->nm_json_array_get (arg_js_obj, i), out_unrecognized_content); if (link_watcher) g_ptr_array_add (link_watchers, link_watcher); } } else { - link_watcher = _link_watcher_from_json (arg_js_obj, + link_watcher = _link_watcher_from_json (vt, + arg_js_obj, out_unrecognized_content); if (link_watcher) { link_watchers = g_ptr_array_new_full (1, (GDestroyNotify) nm_team_link_watcher_unref); @@ -1802,13 +1807,14 @@ _js_parse_unpack (gboolean is_port, if (nm_json_is_array (arg_js_obj)) { gsize i, len; - len = nm_json_array_size (arg_js_obj); + len = vt->nm_json_array_size (arg_js_obj); if (len > 0) { strv = g_ptr_array_sized_new (len); for (i = 0; i < len; i++) { const char *v_string; - if ( nm_jansson_json_as_string (nm_json_array_get (arg_js_obj, i), + if ( nm_jansson_json_as_string (vt, + vt->nm_json_array_get (arg_js_obj, i), &v_string) <= 0 || !v_string || v_string[0] == '\0') { @@ -1869,9 +1875,10 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) #if WITH_JSON_VALIDATION { nm_auto_decref_json json_t *root_js_obj = NULL; + const NMJsonVt *vt; - if (nm_json_vt ()) - root_js_obj = nm_json_loads (js_str, 0, NULL); + if ((vt = nm_json_vt ())) + root_js_obj = vt->nm_json_loads (js_str, 0, NULL); if ( !root_js_obj || !nm_json_is_object (root_js_obj)) @@ -1884,12 +1891,14 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL; gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free = NULL; - _js_parse_locate_keys (self, + _js_parse_locate_keys (vt, + self, root_js_obj, found_keys, &unrecognized_content); - _js_parse_unpack (self->d.is_port, + _js_parse_unpack (vt, + self->d.is_port, found_keys, has_lst, val_lst, From 7df0229c62b027c833b066fdb0cd2588671cba11 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 9 Jan 2018 11:12:10 +0100 Subject: [PATCH 05/13] libnm: don't include in libnm but use own variants It's error prone to include the header and trying not to use it. Don't include . Instead, redefine our nm variants of everything. Note that we only redefine stuff that is in public headers (like "json_t" typedef). libjansson anyway must not change the struct layout and the like, without breaking all applications. That is because the non-opaque code from the header anyway is part of the applications that include it. Later we will add additional unit test that checks that our redefinition matches to what we had at compile time. --- libnm-core/nm-json.c | 2 +- libnm-core/nm-json.h | 139 ++++++++++++++++++++----------------- libnm-core/nm-team-utils.c | 24 +++---- libnm-core/nm-utils.c | 6 +- 4 files changed, 92 insertions(+), 79 deletions(-) diff --git a/libnm-core/nm-json.c b/libnm-core/nm-json.c index d6dbc0238b..57fc79def0 100644 --- a/libnm-core/nm-json.c +++ b/libnm-core/nm-json.c @@ -41,7 +41,7 @@ _nm_json_vt_internal_load (void) #define TRY_BIND_SYMBOL(symbol) \ G_STMT_START { \ - typeof (symbol) (*_sym) = dlsym (handle, #symbol); \ + void *_sym = dlsym (handle, #symbol); \ \ if (!_sym) \ goto fail_symbol; \ diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h index a585d521c4..5042cf0f80 100644 --- a/libnm-core/nm-json.h +++ b/libnm-core/nm-json.h @@ -8,35 +8,62 @@ /*****************************************************************************/ -#if WITH_JANSSON +#define NM_JSON_REJECT_DUPLICATES 0x1 -#include +typedef enum { + NM_JSON_OBJECT, + NM_JSON_ARRAY, + NM_JSON_STRING, + NM_JSON_INTEGER, + NM_JSON_REAL, + NM_JSON_TRUE, + NM_JSON_FALSE, + NM_JSON_NULL, +} nm_json_type; + +typedef struct nm_json_t { + nm_json_type type; + volatile size_t refcount; +} nm_json_t; + +typedef long long nm_json_int_t; + +#define NM_JSON_ERROR_TEXT_LENGTH 160 +#define NM_JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct nm_json_error_t { + int line; + int column; + int position; + char source[NM_JSON_ERROR_SOURCE_LENGTH]; + char text[NM_JSON_ERROR_TEXT_LENGTH]; +} nm_json_error_t; typedef struct { gboolean loaded; - char *(*nm_json_dumps) (const json_t *json, size_t flags); - const char *(*nm_json_object_iter_key) (void *iter); - const char *(*nm_json_string_value) (const json_t *json); - int (*nm_json_array_append_new) (json_t *json, json_t *value); - int (*nm_json_object_del) (json_t *json, const char *key); - int (*nm_json_object_set_new) (json_t *json, const char *key, json_t *value); - json_int_t (*nm_json_integer_value) (const json_t *json); - json_t *(*nm_json_array) (void); - json_t *(*nm_json_array_get) (const json_t *json, size_t index); - json_t *(*nm_json_false) (void); - json_t *(*nm_json_integer) (json_int_t value); - json_t *(*nm_json_loads) (const char *string, size_t flags, json_error_t *error); - json_t *(*nm_json_object) (void); - json_t *(*nm_json_object_get) (const json_t *json, const char *key); - json_t *(*nm_json_object_iter_value) (void *); - json_t *(*nm_json_string) (const char *value); - json_t *(*nm_json_true) (void); - size_t (*nm_json_array_size) (const json_t *json); - size_t (*nm_json_object_size) (const json_t *json); - void (*nm_json_delete) (json_t *json); - void *(*nm_json_object_iter) (json_t *json); - void *(*nm_json_object_iter_next) (json_t *json, void *iter); - void *(*nm_json_object_key_to_iter) (const char *key); + char *(*nm_json_dumps) (const nm_json_t *json, size_t flags); + const char *(*nm_json_object_iter_key) (void *iter); + const char *(*nm_json_string_value) (const nm_json_t *json); + int (*nm_json_array_append_new) (nm_json_t *json, nm_json_t *value); + int (*nm_json_object_del) (nm_json_t *json, const char *key); + int (*nm_json_object_set_new) (nm_json_t *json, const char *key, nm_json_t *value); + nm_json_int_t (*nm_json_integer_value) (const nm_json_t *json); + nm_json_t *(*nm_json_array) (void); + nm_json_t *(*nm_json_array_get) (const nm_json_t *json, size_t index); + nm_json_t *(*nm_json_false) (void); + nm_json_t *(*nm_json_integer) (nm_json_int_t value); + nm_json_t *(*nm_json_loads) (const char *string, size_t flags, nm_json_error_t *error); + nm_json_t *(*nm_json_object) (void); + nm_json_t *(*nm_json_object_get) (const nm_json_t *json, const char *key); + nm_json_t *(*nm_json_object_iter_value) (void *); + nm_json_t *(*nm_json_string) (const char *value); + nm_json_t *(*nm_json_true) (void); + size_t (*nm_json_array_size) (const nm_json_t *json); + size_t (*nm_json_object_size) (const nm_json_t *json); + void (*nm_json_delete) (nm_json_t *json); + void *(*nm_json_object_iter) (nm_json_t *json); + void *(*nm_json_object_iter_next) (nm_json_t *json, void *iter); + void *(*nm_json_object_key_to_iter) (const char *key); } NMJsonVt; extern const NMJsonVt *_nm_json_vt_ptr; @@ -83,7 +110,7 @@ const NMJsonVt *nmtst_json_vt_reset (gboolean loaded); ((val) ? (vt)->nm_json_true () : (vt)->nm_json_false ()) static inline void -nm_json_decref (const NMJsonVt *vt, json_t *json) +nm_json_decref (const NMJsonVt *vt, nm_json_t *json) { /* Our ref-counting is not threadsafe, unlike libjansson's. But we never * share one json_t instance between threads, and if we would, we would very likely @@ -95,7 +122,7 @@ nm_json_decref (const NMJsonVt *vt, json_t *json) } static inline void -_nm_auto_decref_json (json_t **p_json) +_nm_auto_decref_json (nm_json_t **p_json) { if ( *p_json && (*p_json)->refcount != (size_t) -1 @@ -111,13 +138,18 @@ _nm_auto_decref_json (json_t **p_json) * They can be used directly, however, add a nm_json* variant, * to make it explict we don't accidentally use jansson ABI. */ -#define nm_json_is_boolean(json) json_is_boolean (json) -#define nm_json_is_integer(json) json_is_integer (json) -#define nm_json_is_string(json) json_is_string (json) -#define nm_json_is_object(json) json_is_object (json) -#define nm_json_is_array(json) json_is_array (json) -#define nm_json_is_true(json) json_is_true (json) -#define nm_json_boolean_value(json) json_boolean_value (json) +#define nm_json_typeof(json) ((json)->type) +#define nm_json_is_object(json) ((json) && nm_json_typeof(json) == NM_JSON_OBJECT) +#define nm_json_is_array(json) ((json) && nm_json_typeof(json) == NM_JSON_ARRAY) +#define nm_json_is_string(json) ((json) && nm_json_typeof(json) == NM_JSON_STRING) +#define nm_json_is_integer(json) ((json) && nm_json_typeof(json) == NM_JSON_INTEGER) +#define nm_json_is_real(json) ((json) && nm_json_typeof(json) == NM_JSON_REAL) +#define nm_json_is_number(json) (nm_json_is_integer(json) || nm_json_is_real(json)) +#define nm_json_is_true(json) ((json) && nm_json_typeof(json) == NM_JSON_TRUE) +#define nm_json_is_false(json) ((json) && nm_json_typeof(json) == NM_JSON_FALSE) +#define nm_json_boolean_value nm_json_is_true +#define nm_json_is_boolean(json) (nm_json_is_true(json) || nm_json_is_false(json)) +#define nm_json_is_null(json) ((json) && nm_json_typeof(json) == NM_JSON_NULL) #define nm_json_object_foreach(vt, object, key, value) \ for(key = vt->nm_json_object_iter_key (vt->nm_json_object_iter (object)); \ @@ -126,43 +158,26 @@ _nm_auto_decref_json (json_t **p_json) /*****************************************************************************/ -/* Added in Jansson v2.7 */ -#ifndef json_boolean_value -#define json_boolean_value json_is_true -#endif - -/* Added in Jansson v2.8 */ -#ifndef json_object_foreach_safe -#define json_object_foreach_safe(object, n, key, value) \ - for (key = json_object_iter_key(json_object_iter(object)), \ - n = json_object_iter_next(object, json_object_key_to_iter(key)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(n), \ - n = json_object_iter_next(object, json_object_key_to_iter(key))) -#endif - -/*****************************************************************************/ - static inline int -nm_jansson_json_as_bool (const json_t *elem, +nm_jansson_json_as_bool (const nm_json_t *elem, bool *out_val) { if (!elem) return 0; - if (!json_is_boolean (elem)) + if (!nm_json_is_boolean (elem)) return -EINVAL; - NM_SET_OUT (out_val, json_boolean_value (elem)); + NM_SET_OUT (out_val, nm_json_boolean_value (elem)); return 1; } static inline int nm_jansson_json_as_int32 (const NMJsonVt *vt, - const json_t *elem, + const nm_json_t *elem, gint32 *out_val) { - json_int_t v; + nm_json_int_t v; if (!elem) return 0; @@ -181,10 +196,10 @@ nm_jansson_json_as_int32 (const NMJsonVt *vt, static inline int nm_jansson_json_as_int (const NMJsonVt *vt, - const json_t *elem, + const nm_json_t *elem, int *out_val) { - json_int_t v; + nm_json_int_t v; if (!elem) return 0; @@ -203,13 +218,13 @@ nm_jansson_json_as_int (const NMJsonVt *vt, static inline int nm_jansson_json_as_string (const NMJsonVt *vt, - const json_t *elem, + const nm_json_t *elem, const char **out_val) { if (!elem) return 0; - if (!json_is_string (elem)) + if (!nm_json_is_string (elem)) return -EINVAL; NM_SET_OUT (out_val, vt->nm_json_string_value (elem)); @@ -225,7 +240,7 @@ nm_jansson_json_as_string (const NMJsonVt *vt, static inline gboolean nm_value_type_from_json (const NMJsonVt *vt, NMValueType value_type, - const json_t *elem, + const nm_json_t *elem, gpointer out_val) { switch (value_type) { @@ -246,6 +261,4 @@ nm_value_type_from_json (const NMJsonVt *vt, #endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ -#endif /* WITH_JANSSON */ - #endif /* __NM_JSON_H__ */ diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index 43dacaaf71..758766a251 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -1119,19 +1119,19 @@ _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher, #if WITH_JSON_VALIDATION static NMTeamLinkWatcher * _link_watcher_from_json (const NMJsonVt *vt, - const json_t *root_js_obj, + const nm_json_t *root_js_obj, gboolean *out_unrecognized_content) { NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)] = { }; const char *j_key; - json_t *j_val; + nm_json_t *j_val; const char *v_name; NMTeamLinkWatcher *result = NULL; if (!nm_json_is_object (root_js_obj)) goto fail; - nm_json_object_foreach (vt, (json_t *) root_js_obj, j_key, j_val) { + nm_json_object_foreach (vt, (nm_json_t *) root_js_obj, j_key, j_val) { const LinkWatcherAttrData *attr_data = NULL; NMValueTypUnioMaybe *parse_result; @@ -1689,17 +1689,17 @@ _attr_data_find_by_json_key (gboolean is_port, static void _js_parse_locate_keys (const NMJsonVt *vt, NMTeamSetting *self, - json_t *root_js_obj, - json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], + nm_json_t *root_js_obj, + nm_json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], gboolean *out_unrecognized_content) { const char *keys[3]; const char *cur_key1; const char *cur_key2; const char *cur_key3; - json_t *cur_val1; - json_t *cur_val2; - json_t *cur_val3; + nm_json_t *cur_val1; + nm_json_t *cur_val2; + nm_json_t *cur_val3; #define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content) \ ({ \ @@ -1741,7 +1741,7 @@ _js_parse_locate_keys (const NMJsonVt *vt, static void _js_parse_unpack (const NMJsonVt *vt, gboolean is_port, - json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], + nm_json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], bool out_has_lst[static _NM_TEAM_ATTRIBUTE_NUM], NMValueTypUnion out_val_lst[static _NM_TEAM_ATTRIBUTE_NUM], gboolean *out_unrecognized_content, @@ -1753,7 +1753,7 @@ _js_parse_unpack (const NMJsonVt *vt, for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { NMValueTypUnion *p_out_val; gboolean valid = FALSE; - json_t *arg_js_obj; + nm_json_t *arg_js_obj; if (!_team_attr_data_is_relevant (attr_data, is_port)) continue; @@ -1874,7 +1874,7 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) #if WITH_JSON_VALIDATION { - nm_auto_decref_json json_t *root_js_obj = NULL; + nm_auto_decref_json nm_json_t *root_js_obj = NULL; const NMJsonVt *vt; if ((vt = nm_json_vt ())) @@ -1887,7 +1887,7 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) gboolean unrecognized_content = FALSE; bool has_lst[_NM_TEAM_ATTRIBUTE_NUM] = { FALSE, }; NMValueTypUnion val_lst[_NM_TEAM_ATTRIBUTE_NUM]; - json_t *found_keys[_NM_TEAM_ATTRIBUTE_NUM] = { NULL, }; + nm_json_t *found_keys[_NM_TEAM_ATTRIBUTE_NUM] = { NULL, }; gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL; gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free = NULL; diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 8b6b121332..f188ec8250 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -5494,9 +5494,9 @@ gboolean nm_utils_is_json_object (const char *str, GError **error) { #if WITH_JSON_VALIDATION - nm_auto_decref_json json_t *json = NULL; + nm_auto_decref_json nm_json_t *json = NULL; const NMJsonVt *vt; - json_error_t jerror; + nm_json_error_t jerror; g_return_val_if_fail (!error || !*error, FALSE); @@ -5511,7 +5511,7 @@ nm_utils_is_json_object (const char *str, GError **error) if (!(vt = nm_json_vt ())) return _nm_utils_is_json_object_no_validation (str, error); - json = vt->nm_json_loads (str, JSON_REJECT_DUPLICATES, &jerror); + json = vt->nm_json_loads (str, NM_JSON_REJECT_DUPLICATES, &jerror); if (!json) { g_set_error (error, NM_CONNECTION_ERROR, From 3814467b88afa64733f445ebb013619223a72657 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 17:58:44 +0200 Subject: [PATCH 06/13] libnm,shared: move nm-json.[hc] to shared/nm-glib-aux nm-json.[hc] uses libjansson, but only loads it at runtime with dlopen. There is no more run compile time dependency. Move it to shared, so that it can be (theoretically) used by other components. Also, drop the conditional compilation. Granted, if you don't build with libjansson enabled, then the JANSSON_SONAME define is unset and the code will fail to load at runtime (which is fine). However, we can still build against our JSON wrappers. The code savings of conditional build are minimal so drop it. --- Makefile.am | 14 ++------------ libnm-core/meson.build | 6 ------ libnm-core/nm-team-utils.c | 2 +- libnm-core/nm-utils.c | 5 +---- shared/meson.build | 1 + {libnm-core => shared/nm-glib-aux}/nm-json.c | 0 {libnm-core => shared/nm-glib-aux}/nm-json.h | 0 7 files changed, 5 insertions(+), 23 deletions(-) rename {libnm-core => shared/nm-glib-aux}/nm-json.c (100%) rename {libnm-core => shared/nm-glib-aux}/nm-json.h (100%) diff --git a/Makefile.am b/Makefile.am index e6d55b0bc1..7f694d755a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -403,6 +403,8 @@ shared_nm_glib_aux_libnm_glib_aux_la_SOURCES = \ shared/nm-glib-aux/nm-jansson.h \ shared/nm-glib-aux/nm-json-aux.c \ shared/nm-glib-aux/nm-json-aux.h \ + shared/nm-glib-aux/nm-json.c \ + shared/nm-glib-aux/nm-json.h \ shared/nm-glib-aux/nm-keyfile-aux.c \ shared/nm-glib-aux/nm-keyfile-aux.h \ shared/nm-glib-aux/nm-logging-base.c \ @@ -1031,13 +1033,6 @@ libnm_core_lib_c_real = \ libnm-core/nm-vpn-plugin-info.c \ $(NULL) -if WITH_JSON_VALIDATION -libnm_core_lib_h_priv += \ - libnm-core/nm-json.h -libnm_core_lib_c_real += \ - libnm-core/nm-json.c -endif - libnm_core_lib_c_mkenums = \ libnm-core/nm-core-enum-types.c @@ -1092,11 +1087,6 @@ libnm_core_libnm_core_la_CPPFLAGS = \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE \ $(NULL) -if WITH_JSON_VALIDATION -libnm_core_libnm_core_la_CPPFLAGS += $(JANSSON_CFLAGS) -libnm_core_libnm_core_la_CPPFLAGS += -fcommon -endif - libnm_core_libnm_core_la_SOURCES = \ $(libnm_core_lib_h_pub_real) \ $(libnm_core_lib_h_priv) \ diff --git a/libnm-core/meson.build b/libnm-core/meson.build index 89acaf72ad..0509deaa54 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -196,12 +196,6 @@ links = [ libnm_core_c_args = common_c_flags -if enable_json_validation - libnm_core_sources += files('nm-json.c') - deps += jansson_dep - libnm_core_c_args += ['-fcommon'] -endif - libnm_core = static_library( 'nm-core', sources: libnm_core_sources + libnm_core_enum_sources + nm_meta_setting_source + [nm_version_macro_header], diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index 758766a251..1e0ddc79c8 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -11,7 +11,7 @@ #include "nm-errors.h" #include "nm-utils-private.h" -#include "nm-json.h" +#include "nm-glib-aux/nm-json.h" #include "nm-glib-aux/nm-json-aux.h" #include "nm-core-internal.h" #include "nm-setting-team.h" diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index f188ec8250..d04dca5596 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -16,10 +16,7 @@ #include #include -#if WITH_JSON_VALIDATION -#include "nm-json.h" -#endif - +#include "nm-glib-aux/nm-json.h" #include "nm-glib-aux/nm-str-buf.h" #include "nm-glib-aux/nm-enum-utils.h" #include "nm-glib-aux/nm-time-utils.h" diff --git a/shared/meson.build b/shared/meson.build index 0f46a00cbb..0041c5cba3 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -134,6 +134,7 @@ sources = files( 'nm-glib-aux/nm-hash-utils.c', 'nm-glib-aux/nm-io-utils.c', 'nm-glib-aux/nm-json-aux.c', + 'nm-glib-aux/nm-json.c', 'nm-glib-aux/nm-keyfile-aux.c', 'nm-glib-aux/nm-logging-base.c', 'nm-glib-aux/nm-random-utils.c', diff --git a/libnm-core/nm-json.c b/shared/nm-glib-aux/nm-json.c similarity index 100% rename from libnm-core/nm-json.c rename to shared/nm-glib-aux/nm-json.c diff --git a/libnm-core/nm-json.h b/shared/nm-glib-aux/nm-json.h similarity index 100% rename from libnm-core/nm-json.h rename to shared/nm-glib-aux/nm-json.h From bbb1f5df2f2322b909d29921120a9bb1295ca326 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 18:38:22 +0200 Subject: [PATCH 07/13] libnm: always build libnm with JSON validation We anyway load libjansson with dlopen(), and already before it could happen that libjansson is not available. In that case, we would not crash, but simply proceed without json validation. Since libnm-core no longer uses libjansson directly, but only via "nm-glib-aux/nm-json.h", we can just always compile with that, and use it at runtime. That means, libjansson is not a build dependency for libnm anymore, so we don't need a compile time check. Note that if you build without libjansson, then JANSSON_SONAME is undefined, and loading it will still fail at runtime. So, even if we now always build with all our code enabled, it only works if you actually build with libjansson. Still, it's simpler to drop the conditional build, as the only benefit is a (minimally) smaller build. --- config.h.meson | 3 - configure.ac | 19 +- libnm-core/nm-team-utils.c | 28 ++- libnm-core/nm-utils.c | 14 -- libnm-core/tests/test-general.c | 181 ++++++++++-------- libnm-core/tests/test-keyfile.c | 3 +- libnm-core/tests/test-setting.c | 16 +- meson.build | 10 +- meson_options.txt | 1 - .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 3 +- 10 files changed, 124 insertions(+), 154 deletions(-) diff --git a/config.h.meson b/config.h.meson index b421ee1e71..bb5458aa64 100644 --- a/config.h.meson +++ b/config.h.meson @@ -212,9 +212,6 @@ /* Define if JANSSON is enabled */ #mesondefine WITH_JANSSON -/* Define if JSON validation in libnm is enabled */ -#mesondefine WITH_JSON_VALIDATION - /* Define if you have libpsl */ #mesondefine WITH_LIBPSL diff --git a/configure.ac b/configure.ac index 1e6823b494..4ea4dbf081 100644 --- a/configure.ac +++ b/configure.ac @@ -596,6 +596,8 @@ if test "$have_jansson" = "yes"; then fi AC_DEFINE_UNQUOTED(JANSSON_SONAME, "$JANSSON_SONAME", [Define to path to the Jansson shared library]) else + have_jansson=no + JANSSON_SONAME= AC_DEFINE(WITH_JANSSON, 0, [Define if JANSSON is enabled]) fi AM_CONDITIONAL(WITH_JANSSON, test "${have_jansson}" = "yes") @@ -622,21 +624,6 @@ if test "${enable_teamdctl}" = "yes"; then fi AM_CONDITIONAL(WITH_TEAMDCTL, test "${enable_teamdctl}" = "yes") -# Jansson for team configuration validation -AC_ARG_ENABLE(json-validation, - AS_HELP_STRING([--enable-json-validation], [Enable JSON validation in libnm]), - [enable_json_validation=${enableval}], - [enable_json_validation=${have_jansson}]) -if test "${enable_json_validation}" = "no"; then - AC_DEFINE(WITH_JSON_VALIDATION, 0, [Define if JSON validation in libnm is enabled]) -else - if test "$have_jansson" = "no"; then - AC_MSG_ERROR([jansson is needed for team configuration validation. Use --disable-json-validation to build without it.]) - fi - AC_DEFINE(WITH_JSON_VALIDATION, 1, [Define if JSON validation in libnm is enabled]) -fi -AM_CONDITIONAL(WITH_JSON_VALIDATION, test "${enable_json_validation}" != "no") - # we usually compile with polkit support. --enable-polkit=yes|no only sets the # default configuration for main.auth-polkit. User can always enable/disable polkit # authorization via config. @@ -1366,6 +1353,7 @@ echo " nmcli: $build_nmcli" echo " nmtui: $build_nmtui" echo " nm-cloud-setup: $with_nm_cloud_setup" echo " iwd: $ac_with_iwd" +echo " jansson: $have_jansson${JANSSON_SONAME:+ (soname: $JANSSON_SONAME)}" echo echo "Configuration plugins (main.plugins=${config_plugins_default})" @@ -1398,7 +1386,6 @@ echo " valgrind: $with_valgrind $with_valgrind_suppressions" echo " code coverage: $enable_code_coverage" echo " LTO: $enable_lto" echo " linker garbage collection: $enable_ld_gc" -echo " JSON validation for libnm: $enable_json_validation" echo " crypto: $with_crypto (have-gnutls: $have_crypto_gnutls, have-nss: $have_crypto_nss)" echo " sanitizers: $sanitizers" echo " Mozilla Public Suffix List: $with_libpsl" diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index 1e0ddc79c8..ec820e99e9 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -1116,7 +1116,6 @@ _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher, g_string_append (gstr, " }"); } -#if WITH_JSON_VALIDATION static NMTeamLinkWatcher * _link_watcher_from_json (const NMJsonVt *vt, const nm_json_t *root_js_obj, @@ -1219,7 +1218,6 @@ fail: *out_unrecognized_content = TRUE; return NULL; } -#endif /*****************************************************************************/ @@ -1642,7 +1640,6 @@ nm_team_setting_config_get (const NMTeamSetting *self) /*****************************************************************************/ -#if WITH_JSON_VALIDATION static gboolean _attr_data_match_keys (const TeamAttrData *attr_data, const char *const*keys, @@ -1701,6 +1698,8 @@ _js_parse_locate_keys (const NMJsonVt *vt, nm_json_t *cur_val2; nm_json_t *cur_val3; + nm_assert (vt); + #define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content) \ ({ \ const TeamAttrData *_attr_data; \ @@ -1750,6 +1749,8 @@ _js_parse_unpack (const NMJsonVt *vt, { const TeamAttrData *attr_data; + nm_assert (vt); + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { NMValueTypUnion *p_out_val; gboolean valid = FALSE; @@ -1838,11 +1839,11 @@ _js_parse_unpack (const NMJsonVt *vt, *out_unrecognized_content = TRUE; } } -#endif guint32 nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) { + const NMJsonVt *vt; guint32 changed_flags = 0; gboolean do_set_default = TRUE; gboolean new_js_str_invalid = FALSE; @@ -1872,14 +1873,10 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) } else changed_flags |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); -#if WITH_JSON_VALIDATION - { + if ((vt = nm_json_vt ())) { nm_auto_decref_json nm_json_t *root_js_obj = NULL; - const NMJsonVt *vt; - - if ((vt = nm_json_vt ())) - root_js_obj = vt->nm_json_loads (js_str, 0, NULL); + root_js_obj = vt->nm_json_loads (js_str, 0, NULL); if ( !root_js_obj || !nm_json_is_object (root_js_obj)) new_js_str_invalid = TRUE; @@ -1915,8 +1912,6 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) } } -#endif - if (do_set_default) changed_flags |= _team_setting_set_default (self); @@ -2231,6 +2226,7 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self, GVariantIter iter; const char *v_key; GVariant *v_val; + const NMJsonVt *vt; *out_changed = 0; @@ -2280,10 +2276,12 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self, variants[attr_data->team_attr] = g_steal_pointer (&v_val_free); } + vt = nm_json_vt (); + if (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) { if ( variants[NM_TEAM_ATTRIBUTE_CONFIG] - && WITH_JSON_VALIDATION + && vt && !NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) { /* we don't require the content of the "link-watchers" and we also * don't perform strict validation. No need to parse it. */ @@ -2291,7 +2289,7 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self, gs_free_error GError *local = NULL; /* We might need the parsed v_link_watchers array below (because there is no JSON - * "config" present or because we don't build WITH_JSON_VALIDATION). + * "config" present or because we don't have json support). * * Or we might run with NM_SETTING_PARSE_FLAGS_STRICT. In that mode, we may not necessarily * require that the entire setting as a whole validates (if a JSON config is present and @@ -2319,7 +2317,7 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self, ? g_variant_get_string (variants[NM_TEAM_ATTRIBUTE_CONFIG], NULL) : NULL); - if ( WITH_JSON_VALIDATION + if ( vt && variants[NM_TEAM_ATTRIBUTE_CONFIG]) { /* for team settings, the JSON must be able to express all possible options. That means, * if the GVariant contains both the JSON "config" and other options, then the other options diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d04dca5596..069c00e77c 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -5490,7 +5490,6 @@ _nm_utils_is_json_object_no_validation (const char *str, GError **error) gboolean nm_utils_is_json_object (const char *str, GError **error) { -#if WITH_JSON_VALIDATION nm_auto_decref_json nm_json_t *json = NULL; const NMJsonVt *vt; nm_json_error_t jerror; @@ -5530,19 +5529,6 @@ nm_utils_is_json_object (const char *str, GError **error) } return TRUE; -#else /* !WITH_JSON_VALIDATION */ - g_return_val_if_fail (!error || !*error, FALSE); - - if (!str || !str[0]) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - str ? _("value is NULL") : _("value is empty")); - return FALSE; - } - - return _nm_utils_is_json_object_no_validation (str, error); -#endif } static char * diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 040bcb9f1f..0d2629eabe 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -14,6 +14,7 @@ #include "nm-std-aux/c-list-util.h" #include "nm-glib-aux/nm-enum-utils.h" #include "nm-glib-aux/nm-str-buf.h" +#include "nm-glib-aux/nm-json.h" #include "systemd/nm-sd-utils-shared.h" #include "nm-utils.h" @@ -7125,17 +7126,19 @@ test_nm_utils_check_valid_json (void) { _json_config_check_valid (NULL, FALSE); _json_config_check_valid ("", FALSE); -#if WITH_JSON_VALIDATION - _json_config_check_valid ("{ }", TRUE); - _json_config_check_valid ("{ \"a\" : 1 }", TRUE); - _json_config_check_valid ("{ \"a\" : }", FALSE); -#else + /* Without JSON library everything except empty string is considered valid */ + nmtst_json_vt_reset (FALSE); _json_config_check_valid ("{ }", TRUE); _json_config_check_valid ("{'%!-a1} ", TRUE); _json_config_check_valid (" {'%!-a1}", TRUE); _json_config_check_valid ("{'%!-a1", FALSE); -#endif + + if (nmtst_json_vt_reset (TRUE)) { + _json_config_check_valid ("{ }", TRUE); + _json_config_check_valid ("{ \"a\" : 1 }", TRUE); + _json_config_check_valid ("{ \"a\" : }", FALSE); + } } static void @@ -7172,86 +7175,96 @@ _team_config_equal_check (const char *conf1, static void test_nm_utils_team_config_equal (void) { - _team_config_equal_check ("", - "", - TRUE, - TRUE); - _team_config_equal_check ("", - " ", - TRUE, - TRUE); - _team_config_equal_check ("{}", - "{ }", - TRUE, - TRUE); - _team_config_equal_check ("{}", - "{", - TRUE, - TRUE); - _team_config_equal_check ("{ \"a\": 1 }", - "{ \"a\": 1 }", - TRUE, - TRUE); - _team_config_equal_check ("{ \"a\": 1 }", - "{ \"a\": 1 }", - TRUE, - TRUE); + int with_json_vt; - /* team config */ - _team_config_equal_check ("{ }", - "{ \"runner\" : { \"name\" : \"random\"} }", - FALSE, - !WITH_JSON_VALIDATION); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }", - "{ \"runner\" : { \"name\" : \"random\"} }", - FALSE, - !WITH_JSON_VALIDATION); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"} }", - "{ \"runner\" : { \"name\" : \"random\"} }", - FALSE, - TRUE); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"loadbalance\"} }", - "{ \"runner\" : { \"name\" : \"loadbalance\"} }", - FALSE, - TRUE); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth0\" : {} } }", - "{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth1\" : {} } }", - FALSE, - TRUE); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }", - "{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }", - FALSE, - !WITH_JSON_VALIDATION); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }", - "{ \"runner\" : { \"name\" : \"roundrobin\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }", - FALSE, - !WITH_JSON_VALIDATION); - _team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }", - "{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\" ] } }", - FALSE, - !WITH_JSON_VALIDATION); + for (with_json_vt = 0; with_json_vt < 2; with_json_vt++) { + const NMJsonVt *vt; - /* team port config */ - _team_config_equal_check ("{ }", - "{ \"link_watch\" : { \"name\" : \"ethtool\"} }", - TRUE, - !WITH_JSON_VALIDATION); - _team_config_equal_check ("{ }", - "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", - TRUE, - TRUE); - _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"ethtool\"} }", - "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", - TRUE, - !WITH_JSON_VALIDATION); - _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", - "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", - TRUE, - TRUE); - _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth0\" : {} } }", - "{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth1\" : {} } }", - TRUE, - TRUE); + vt = nmtst_json_vt_reset (!!with_json_vt); + + _team_config_equal_check ("", + "", + TRUE, + TRUE); + _team_config_equal_check ("", + " ", + TRUE, + TRUE); + _team_config_equal_check ("{}", + "{ }", + TRUE, + TRUE); + _team_config_equal_check ("{}", + "{", + TRUE, + TRUE); + _team_config_equal_check ("{ \"a\": 1 }", + "{ \"a\": 1 }", + TRUE, + TRUE); + _team_config_equal_check ("{ \"a\": 1 }", + "{ \"a\": 1 }", + TRUE, + TRUE); + + /* team config */ + _team_config_equal_check ("{ }", + "{ \"runner\" : { \"name\" : \"random\"} }", + FALSE, + !vt); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }", + "{ \"runner\" : { \"name\" : \"random\"} }", + FALSE, + !vt); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"} }", + "{ \"runner\" : { \"name\" : \"random\"} }", + FALSE, + TRUE); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"loadbalance\"} }", + "{ \"runner\" : { \"name\" : \"loadbalance\"} }", + FALSE, + TRUE); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth0\" : {} } }", + "{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth1\" : {} } }", + FALSE, + TRUE); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }", + "{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }", + FALSE, + !vt); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }", + "{ \"runner\" : { \"name\" : \"roundrobin\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }", + FALSE, + !vt); + _team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }", + "{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\" ] } }", + FALSE, + !vt); + + /* team port config */ + _team_config_equal_check ("{ }", + "{ \"link_watch\" : { \"name\" : \"ethtool\"} }", + TRUE, + !vt); + _team_config_equal_check ("{ }", + "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", + TRUE, + TRUE); + _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"ethtool\"} }", + "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", + TRUE, + !vt); + _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", + "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", + TRUE, + TRUE); + _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth0\" : {} } }", + "{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth1\" : {} } }", + TRUE, + TRUE); + } + + nmtst_json_vt_reset (TRUE); } /*****************************************************************************/ diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index 23149b618d..c740a9068d 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -5,6 +5,7 @@ #include "nm-default.h" +#include "nm-glib-aux/nm-json.h" #include "nm-keyfile/nm-keyfile-utils.h" #include "nm-keyfile/nm-keyfile-internal.h" #include "nm-simple-connection.h" @@ -627,7 +628,7 @@ test_team_conf_read_invalid (void) gs_unref_object NMConnection *con = NULL; NMSettingTeam *s_team; - if (!WITH_JSON_VALIDATION) { + if (!nm_json_vt ()) { g_test_skip ("team test requires JSON validation"); return; } diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 1a32d91a49..3b59d7fa57 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -8,6 +8,7 @@ #include #include +#include "nm-glib-aux/nm-json.h" #include "nm-core-internal.h" #include "nm-utils.h" #include "nm-utils-private.h" @@ -32,15 +33,6 @@ /*****************************************************************************/ -/* assert that the define is just a plain integer (boolean). */ - -G_STATIC_ASSERT ( (WITH_JSON_VALIDATION) == 1 - || (WITH_JSON_VALIDATION) == 0); - -_nm_unused static const int _with_json_validation = WITH_JSON_VALIDATION; - -/*****************************************************************************/ - /* converts @dict to a connection. In this case, @dict must be good, without warnings, so that * NM_SETTING_PARSE_FLAGS_STRICT and NM_SETTING_PARSE_FLAGS_BEST_EFFORT yield the exact same results. */ static NMConnection * @@ -1000,7 +992,7 @@ _test_team_config_sync (const char *team_config, guint i, j; gboolean found; - if (!WITH_JSON_VALIDATION) { + if (!nm_json_vt ()) { g_test_skip ("team test requires JSON validation"); return; } @@ -1265,7 +1257,7 @@ _test_team_port_config_sync (const char *team_port_config, guint i, j; gboolean found; - if (!WITH_JSON_VALIDATION) { + if (!nm_json_vt ()) { g_test_skip ("team test requires JSON validation"); return; } @@ -1397,7 +1389,7 @@ _check_team_setting (NMSetting *setting) : nm_setting_team_get_config (NM_SETTING_TEAM (setting)), NULL); - if (WITH_JSON_VALIDATION) + if (nm_json_vt ()) nmtst_assert_setting_is_equal (setting, setting2, NM_SETTING_COMPARE_FLAG_EXACT); g_clear_object (&setting2); diff --git a/meson.build b/meson.build index 8eed507f08..28cd4f3028 100644 --- a/meson.build +++ b/meson.build @@ -256,6 +256,7 @@ libndp_dep = dependency('libndp') jansson_dep = dependency('jansson', version: '>= 2.5', required: false) config_h.set10('WITH_JANSSON', jansson_dep.found()) +jansson_msg = 'no' if jansson_dep.found() jansson_libdir = jansson_dep.get_pkgconfig_variable('libdir') res = run_command(find_program('eu-readelf', 'readelf'), '-d', join_paths(jansson_libdir, 'libjansson.so')) @@ -267,6 +268,7 @@ if jansson_dep.found() endforeach assert(jansson_soname != '', 'Unable to determine Jansson SONAME') config_h.set_quoted('JANSSON_SONAME', jansson_soname) + jansson_msg = 'yes (soname: ' + jansson_soname + ')' endif libsystemd_dep = dependency('libsystemd', version: '>= 209', required: false) @@ -497,12 +499,6 @@ if enable_teamdctl assert(libteamdctl_dep.found(), 'You must have libteamdctl installed to build. Use -Dteamdctl=false to disable it') endif -enable_json_validation = get_option('json_validation') -if enable_json_validation - assert(jansson_dep.found(), 'jansson is needed for team configuration validation. Use -Djson_validation=false to disable it') -endif -config_h.set10('WITH_JSON_VALIDATION', enable_json_validation) - # polkit enable_polkit = get_option('polkit') if enable_polkit @@ -1013,6 +1009,7 @@ if enable_ppp output += ' ' + pppd_path + ' plugins:' + pppd_plugin_dir endif output += '\n' +output += ' jansson: ' + jansson_msg + '\n' output += ' modemmanager-1: ' + enable_modem_manager.to_string() + '\n' output += ' ofono: ' + enable_ofono.to_string() + '\n' output += ' concheck: ' + enable_concheck.to_string() + '\n' @@ -1047,7 +1044,6 @@ output += '\n' output += ' code coverage: ' + get_option('b_coverage').to_string() + '\n' output += ' LTO: ' + enable_lto.to_string() + '\n' output += ' Linker garbage collection: ' + enable_ld_gc.to_string() + '\n' -output += ' JSON validation for libnm: ' + enable_json_validation.to_string() + '\n' output += ' crypto: ' + crypto + '\n' output += ' sanitizers: ' + get_option('b_sanitize') + '\n' output += ' Mozilla Public Suffix List: ' + enable_libpsl.to_string() + '\n' diff --git a/meson_options.txt b/meson_options.txt index a5c6a22fb0..d6306711ec 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -69,6 +69,5 @@ option('valgrind', type: 'array', value: ['no'], description: 'Use valgrind to m option('valgrind_suppressions', type: 'string', value: '', description: 'Use specific valgrind suppression file') option('ld_gc', type: 'boolean', value: true, description: 'Enable garbage collection of unused symbols on linking') option('libpsl', type: 'boolean', value: true, description: 'Link against libpsl') -option('json_validation', type: 'boolean', value: true, description: 'Enable JSON validation in libnm') option('crypto', type: 'combo', choices: ['nss', 'gnutls'], value: 'nss', description: 'Cryptography library to use for certificate and key operations') option('qt', type: 'boolean', value: true, description: 'enable Qt examples') diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 934c4901f9..b8fdd922ce 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -15,6 +15,7 @@ #include #include +#include "nm-glib-aux/nm-json.h" #include "nm-utils.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" @@ -9129,7 +9130,7 @@ test_read_team_master_invalid (gconstpointer user_data) gs_free_error GError *error = NULL; gs_unref_object NMConnection *connection = NULL; - if (WITH_JSON_VALIDATION) { + if (nm_json_vt ()) { _connection_from_file_fail (PATH_NAME, NULL, TYPE_ETHERNET, &error); g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); From 4a7da1ca4b52dc827d9c0bcd1dc76fd507bfe2e1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 19:26:33 +0200 Subject: [PATCH 08/13] shared: merge nm-glib-aux/nm-json.[hc] into nm-json-aux.[hc] They serve a similar purpose. Previously, nm-json-aux.h contained the virtual function table for accessing the dynamically loaded libjansson. But there is no reason why our own helper functions from nm-json.h cannot be there too. --- Makefile.am | 2 - libnm-core/nm-team-utils.c | 1 - libnm-core/nm-utils.c | 2 +- libnm-core/tests/test-general.c | 2 +- libnm-core/tests/test-keyfile.c | 2 +- libnm-core/tests/test-setting.c | 2 +- shared/meson.build | 1 - shared/nm-glib-aux/nm-json-aux.c | 130 ++++++++- shared/nm-glib-aux/nm-json-aux.h | 263 ++++++++++++++++- shared/nm-glib-aux/nm-json.c | 134 --------- shared/nm-glib-aux/nm-json.h | 264 ------------------ .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 2 +- 12 files changed, 392 insertions(+), 413 deletions(-) delete mode 100644 shared/nm-glib-aux/nm-json.c delete mode 100644 shared/nm-glib-aux/nm-json.h diff --git a/Makefile.am b/Makefile.am index 7f694d755a..ad371c4247 100644 --- a/Makefile.am +++ b/Makefile.am @@ -403,8 +403,6 @@ shared_nm_glib_aux_libnm_glib_aux_la_SOURCES = \ shared/nm-glib-aux/nm-jansson.h \ shared/nm-glib-aux/nm-json-aux.c \ shared/nm-glib-aux/nm-json-aux.h \ - shared/nm-glib-aux/nm-json.c \ - shared/nm-glib-aux/nm-json.h \ shared/nm-glib-aux/nm-keyfile-aux.c \ shared/nm-glib-aux/nm-keyfile-aux.h \ shared/nm-glib-aux/nm-logging-base.c \ diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index ec820e99e9..ccdefecbf7 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -11,7 +11,6 @@ #include "nm-errors.h" #include "nm-utils-private.h" -#include "nm-glib-aux/nm-json.h" #include "nm-glib-aux/nm-json-aux.h" #include "nm-core-internal.h" #include "nm-setting-team.h" diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 069c00e77c..b633a5099c 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -16,7 +16,7 @@ #include #include -#include "nm-glib-aux/nm-json.h" +#include "nm-glib-aux/nm-json-aux.h" #include "nm-glib-aux/nm-str-buf.h" #include "nm-glib-aux/nm-enum-utils.h" #include "nm-glib-aux/nm-time-utils.h" diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 0d2629eabe..1e4efe2452 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -14,7 +14,7 @@ #include "nm-std-aux/c-list-util.h" #include "nm-glib-aux/nm-enum-utils.h" #include "nm-glib-aux/nm-str-buf.h" -#include "nm-glib-aux/nm-json.h" +#include "nm-glib-aux/nm-json-aux.h" #include "systemd/nm-sd-utils-shared.h" #include "nm-utils.h" diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index c740a9068d..c9ec51f01b 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -5,7 +5,7 @@ #include "nm-default.h" -#include "nm-glib-aux/nm-json.h" +#include "nm-glib-aux/nm-json-aux.h" #include "nm-keyfile/nm-keyfile-utils.h" #include "nm-keyfile/nm-keyfile-internal.h" #include "nm-simple-connection.h" diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 3b59d7fa57..5ec0ccbdb1 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -8,7 +8,7 @@ #include #include -#include "nm-glib-aux/nm-json.h" +#include "nm-glib-aux/nm-json-aux.h" #include "nm-core-internal.h" #include "nm-utils.h" #include "nm-utils-private.h" diff --git a/shared/meson.build b/shared/meson.build index 0041c5cba3..0f46a00cbb 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -134,7 +134,6 @@ sources = files( 'nm-glib-aux/nm-hash-utils.c', 'nm-glib-aux/nm-io-utils.c', 'nm-glib-aux/nm-json-aux.c', - 'nm-glib-aux/nm-json.c', 'nm-glib-aux/nm-keyfile-aux.c', 'nm-glib-aux/nm-logging-base.c', 'nm-glib-aux/nm-random-utils.c', diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c index afa92531c6..17a70edf75 100644 --- a/shared/nm-glib-aux/nm-json-aux.c +++ b/shared/nm-glib-aux/nm-json-aux.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: LGPL-2.1+ /* - * Copyright (C) 2019 Red Hat, Inc. + * Copyright (C) 2017 - 2019 Red Hat, Inc. */ #include "nm-default.h" #include "nm-json-aux.h" +#include + /*****************************************************************************/ static void @@ -133,3 +135,129 @@ nm_json_aux_gstr_append_obj_name (GString *gstr, } else g_string_append (gstr, ": "); } + +/*****************************************************************************/ + +typedef struct { + NMJsonVt vt; + void *dl_handle; +} NMJsonVtInternal; + +static NMJsonVtInternal * +_nm_json_vt_internal_load (void) +{ + NMJsonVtInternal *v; + void *handle = NULL; + int mode; + + v = g_new0 (NMJsonVtInternal, 1); + +#ifndef JANSSON_SONAME +#define JANSSON_SONAME "" +#endif + + mode = RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND; +#if defined (ASAN_BUILD) + /* Address sanitizer is incompatible with RTLD_DEEPBIND. */ + mode &= ~RTLD_DEEPBIND; +#endif + + if (strlen (JANSSON_SONAME) > 0) + handle = dlopen (JANSSON_SONAME, mode); + + if (!handle) + return v; + +#define TRY_BIND_SYMBOL(symbol) \ + G_STMT_START { \ + void *_sym = dlsym (handle, #symbol); \ + \ + if (!_sym) \ + goto fail_symbol; \ + v->vt.nm_ ## symbol = _sym; \ + } G_STMT_END + + TRY_BIND_SYMBOL (json_array); + TRY_BIND_SYMBOL (json_array_append_new); + TRY_BIND_SYMBOL (json_array_get); + TRY_BIND_SYMBOL (json_array_size); + TRY_BIND_SYMBOL (json_delete); + TRY_BIND_SYMBOL (json_dumps); + TRY_BIND_SYMBOL (json_false); + TRY_BIND_SYMBOL (json_integer); + TRY_BIND_SYMBOL (json_integer_value); + TRY_BIND_SYMBOL (json_loads); + TRY_BIND_SYMBOL (json_object); + TRY_BIND_SYMBOL (json_object_del); + TRY_BIND_SYMBOL (json_object_get); + TRY_BIND_SYMBOL (json_object_iter); + TRY_BIND_SYMBOL (json_object_iter_key); + TRY_BIND_SYMBOL (json_object_iter_next); + TRY_BIND_SYMBOL (json_object_iter_value); + TRY_BIND_SYMBOL (json_object_key_to_iter); + TRY_BIND_SYMBOL (json_object_set_new); + TRY_BIND_SYMBOL (json_object_size); + TRY_BIND_SYMBOL (json_string); + TRY_BIND_SYMBOL (json_string_value); + TRY_BIND_SYMBOL (json_true); + + v->vt.loaded = TRUE; + v->dl_handle = handle; + return v; + +fail_symbol: + dlclose (&handle); + *v = (NMJsonVtInternal) { }; + return v; +} + +const NMJsonVt *_nm_json_vt_ptr = NULL; + +const NMJsonVt * +_nm_json_vt_init (void) +{ + NMJsonVtInternal *v; + +again: + v = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); + if (G_UNLIKELY (!v)) { + v = _nm_json_vt_internal_load (); + if (!g_atomic_pointer_compare_and_exchange ((gpointer *) &_nm_json_vt_ptr, NULL, v)) { + if (v->dl_handle) + dlclose (v->dl_handle); + g_free (v); + goto again; + } + + /* we transfer ownership. */ + } + + nm_assert (v && v == g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr)); + return &v->vt; +} + +const NMJsonVt * +nmtst_json_vt_reset (gboolean loaded) +{ + NMJsonVtInternal *v_old; + NMJsonVtInternal *v; + + v_old = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); + + if (!loaded) { + /* load a fake instance for testing. */ + v = g_new0 (NMJsonVtInternal, 1); + } else + v = _nm_json_vt_internal_load (); + + if (!g_atomic_pointer_compare_and_exchange ((gpointer *) &_nm_json_vt_ptr, v_old, v)) + g_assert_not_reached (); + + if (v_old) { + if (v_old->dl_handle) + dlclose (v_old->dl_handle); + g_free ((gpointer *) v_old); + } + + return v->vt.loaded ? &v->vt : NULL; +} diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h index ed3be3768f..def96b6a7c 100644 --- a/shared/nm-glib-aux/nm-json-aux.h +++ b/shared/nm-glib-aux/nm-json-aux.h @@ -1,11 +1,13 @@ // SPDX-License-Identifier: LGPL-2.1+ /* - * Copyright (C) 2019 Red Hat, Inc. + * Copyright (C) 2017 - 2019 Red Hat, Inc. */ #ifndef __NM_JSON_AUX_H__ #define __NM_JSON_AUX_H__ +#include "nm-value-type.h" + /*****************************************************************************/ static inline GString * @@ -42,8 +44,238 @@ void nm_json_aux_gstr_append_obj_name (GString *gstr, /*****************************************************************************/ +#define NM_JSON_REJECT_DUPLICATES 0x1 + +typedef enum { + NM_JSON_OBJECT, + NM_JSON_ARRAY, + NM_JSON_STRING, + NM_JSON_INTEGER, + NM_JSON_REAL, + NM_JSON_TRUE, + NM_JSON_FALSE, + NM_JSON_NULL, +} nm_json_type; + +typedef struct nm_json_t { + nm_json_type type; + volatile size_t refcount; +} nm_json_t; + +typedef long long nm_json_int_t; + +#define NM_JSON_ERROR_TEXT_LENGTH 160 +#define NM_JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct nm_json_error_t { + int line; + int column; + int position; + char source[NM_JSON_ERROR_SOURCE_LENGTH]; + char text[NM_JSON_ERROR_TEXT_LENGTH]; +} nm_json_error_t; + +typedef struct { + gboolean loaded; + char *(*nm_json_dumps) (const nm_json_t *json, size_t flags); + const char *(*nm_json_object_iter_key) (void *iter); + const char *(*nm_json_string_value) (const nm_json_t *json); + int (*nm_json_array_append_new) (nm_json_t *json, nm_json_t *value); + int (*nm_json_object_del) (nm_json_t *json, const char *key); + int (*nm_json_object_set_new) (nm_json_t *json, const char *key, nm_json_t *value); + nm_json_int_t (*nm_json_integer_value) (const nm_json_t *json); + nm_json_t *(*nm_json_array) (void); + nm_json_t *(*nm_json_array_get) (const nm_json_t *json, size_t index); + nm_json_t *(*nm_json_false) (void); + nm_json_t *(*nm_json_integer) (nm_json_int_t value); + nm_json_t *(*nm_json_loads) (const char *string, size_t flags, nm_json_error_t *error); + nm_json_t *(*nm_json_object) (void); + nm_json_t *(*nm_json_object_get) (const nm_json_t *json, const char *key); + nm_json_t *(*nm_json_object_iter_value) (void *); + nm_json_t *(*nm_json_string) (const char *value); + nm_json_t *(*nm_json_true) (void); + size_t (*nm_json_array_size) (const nm_json_t *json); + size_t (*nm_json_object_size) (const nm_json_t *json); + void (*nm_json_delete) (nm_json_t *json); + void *(*nm_json_object_iter) (nm_json_t *json); + void *(*nm_json_object_iter_next) (nm_json_t *json, void *iter); + void *(*nm_json_object_key_to_iter) (const char *key); +} NMJsonVt; + +extern const NMJsonVt *_nm_json_vt_ptr; + +const NMJsonVt *_nm_json_vt_init (void); + +static inline const NMJsonVt * +_nm_json_vt (void) +{ + const NMJsonVt *vt; + + vt = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); + if (G_UNLIKELY (!vt)) { + vt = _nm_json_vt_init (); + nm_assert (vt); + } + return vt; +} + +static inline const NMJsonVt * +nm_json_vt (void) +{ + const NMJsonVt *vt; + + vt = _nm_json_vt(); + return vt->loaded ? vt : NULL; +} + +static inline const NMJsonVt * +nm_json_vt_assert (void) +{ + const NMJsonVt *vt; + + vt = _nm_json_vt(); + nm_assert (vt->loaded); + return vt; +} + +const NMJsonVt *nmtst_json_vt_reset (gboolean loaded); + +/*****************************************************************************/ + +#define nm_json_boolean(vt, val) \ + ((val) ? (vt)->nm_json_true () : (vt)->nm_json_false ()) + +static inline void +nm_json_decref (const NMJsonVt *vt, nm_json_t *json) +{ + /* Our ref-counting is not threadsafe, unlike libjansson's. But we never + * share one json_t instance between threads, and if we would, we would very likely + * wrap a mutex around it. */ + if ( json + && json->refcount != (size_t) -1 + && --json->refcount == 0) + vt->nm_json_delete (json); +} + +static inline void +_nm_auto_decref_json (nm_json_t **p_json) +{ + if ( *p_json + && (*p_json)->refcount != (size_t) -1 + && --(*p_json)->refcount == 0) + nm_json_vt ()->nm_json_delete (*p_json); +} + +#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) + +/*****************************************************************************/ + +/* the following are implemented as pure macros in jansson.h. + * They can be used directly, however, add a nm_json* variant, + * to make it explict we don't accidentally use jansson ABI. */ + +#define nm_json_typeof(json) ((json)->type) +#define nm_json_is_object(json) ((json) && nm_json_typeof(json) == NM_JSON_OBJECT) +#define nm_json_is_array(json) ((json) && nm_json_typeof(json) == NM_JSON_ARRAY) +#define nm_json_is_string(json) ((json) && nm_json_typeof(json) == NM_JSON_STRING) +#define nm_json_is_integer(json) ((json) && nm_json_typeof(json) == NM_JSON_INTEGER) +#define nm_json_is_real(json) ((json) && nm_json_typeof(json) == NM_JSON_REAL) +#define nm_json_is_number(json) (nm_json_is_integer(json) || nm_json_is_real(json)) +#define nm_json_is_true(json) ((json) && nm_json_typeof(json) == NM_JSON_TRUE) +#define nm_json_is_false(json) ((json) && nm_json_typeof(json) == NM_JSON_FALSE) +#define nm_json_boolean_value nm_json_is_true +#define nm_json_is_boolean(json) (nm_json_is_true(json) || nm_json_is_false(json)) +#define nm_json_is_null(json) ((json) && nm_json_typeof(json) == NM_JSON_NULL) + +#define nm_json_array_foreach(vt, array, index, value) \ + for(index = 0; \ + index < vt->nm_json_array_size (array) && (value = vt->nm_json_array_get (array, index)); \ + index++) + +#define nm_json_object_foreach(vt, object, key, value) \ + for(key = vt->nm_json_object_iter_key (vt->nm_json_object_iter (object)); \ + key && (value = vt->nm_json_object_iter_value (vt->nm_json_object_key_to_iter (key))); \ + key = vt->nm_json_object_iter_key (vt->nm_json_object_iter_next (object, vt->nm_json_object_key_to_iter (key)))) + +/*****************************************************************************/ + +static inline int +nm_jansson_json_as_bool (const nm_json_t *elem, + bool *out_val) +{ + if (!elem) + return 0; + + if (!nm_json_is_boolean (elem)) + return -EINVAL; + + NM_SET_OUT (out_val, nm_json_boolean_value (elem)); + return 1; +} + +static inline int +nm_jansson_json_as_int32 (const NMJsonVt *vt, + const nm_json_t *elem, + gint32 *out_val) +{ + nm_json_int_t v; + + if (!elem) + return 0; + + if (!nm_json_is_integer (elem)) + return -EINVAL; + + v = vt->nm_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 NMJsonVt *vt, + const nm_json_t *elem, + int *out_val) +{ + nm_json_int_t v; + + if (!elem) + return 0; + + if (!nm_json_is_integer (elem)) + return -EINVAL; + + v = vt->nm_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 NMJsonVt *vt, + const nm_json_t *elem, + const char **out_val) +{ + if (!elem) + return 0; + + if (!nm_json_is_string (elem)) + return -EINVAL; + + NM_SET_OUT (out_val, vt->nm_json_string_value (elem)); + return 1; +} + +/*****************************************************************************/ + #ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS -#include "nm-value-type.h" + static inline void nm_value_type_to_json (NMValueType value_type, GString *gstr, @@ -62,8 +294,29 @@ nm_value_type_to_json (NMValueType value_type, } nm_assert_not_reached (); } -#endif -/*****************************************************************************/ +static inline gboolean +nm_value_type_from_json (const NMJsonVt *vt, + NMValueType value_type, + const nm_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 (vt, elem, out_val) > 0); + case NM_VALUE_TYPE_INT: return (nm_jansson_json_as_int (vt, elem, out_val) > 0); -#endif /* __NM_JSON_AUX_H__ */ + /* 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 (vt, elem, out_val) > 0); + + case NM_VALUE_TYPE_UNSPEC: + break; + } + nm_assert_not_reached (); + return FALSE; +} + +#endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ + +#endif /* __NM_JSON_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-json.c b/shared/nm-glib-aux/nm-json.c deleted file mode 100644 index 57fc79def0..0000000000 --- a/shared/nm-glib-aux/nm-json.c +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2017, 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-json.h" - -#include - -typedef struct { - NMJsonVt vt; - void *dl_handle; -} NMJsonVtInternal; - -static NMJsonVtInternal * -_nm_json_vt_internal_load (void) -{ - NMJsonVtInternal *v; - void *handle = NULL; - int mode; - - v = g_new0 (NMJsonVtInternal, 1); - -#ifndef JANSSON_SONAME -#define JANSSON_SONAME "" -#endif - - mode = RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND; -#if defined (ASAN_BUILD) - /* Address sanitizer is incompatible with RTLD_DEEPBIND. */ - mode &= ~RTLD_DEEPBIND; -#endif - - if (strlen (JANSSON_SONAME) > 0) - handle = dlopen (JANSSON_SONAME, mode); - - if (!handle) - return v; - -#define TRY_BIND_SYMBOL(symbol) \ - G_STMT_START { \ - void *_sym = dlsym (handle, #symbol); \ - \ - if (!_sym) \ - goto fail_symbol; \ - v->vt.nm_ ## symbol = _sym; \ - } G_STMT_END - - TRY_BIND_SYMBOL (json_array); - TRY_BIND_SYMBOL (json_array_append_new); - TRY_BIND_SYMBOL (json_array_get); - TRY_BIND_SYMBOL (json_array_size); - TRY_BIND_SYMBOL (json_delete); - TRY_BIND_SYMBOL (json_dumps); - TRY_BIND_SYMBOL (json_false); - TRY_BIND_SYMBOL (json_integer); - TRY_BIND_SYMBOL (json_integer_value); - TRY_BIND_SYMBOL (json_loads); - TRY_BIND_SYMBOL (json_object); - TRY_BIND_SYMBOL (json_object_del); - TRY_BIND_SYMBOL (json_object_get); - TRY_BIND_SYMBOL (json_object_iter); - TRY_BIND_SYMBOL (json_object_iter_key); - TRY_BIND_SYMBOL (json_object_iter_next); - TRY_BIND_SYMBOL (json_object_iter_value); - TRY_BIND_SYMBOL (json_object_key_to_iter); - TRY_BIND_SYMBOL (json_object_set_new); - TRY_BIND_SYMBOL (json_object_size); - TRY_BIND_SYMBOL (json_string); - TRY_BIND_SYMBOL (json_string_value); - TRY_BIND_SYMBOL (json_true); - - v->vt.loaded = TRUE; - v->dl_handle = handle; - return v; - -fail_symbol: - dlclose (&handle); - *v = (NMJsonVtInternal) { }; - return v; -} - -const NMJsonVt *_nm_json_vt_ptr = NULL; - -const NMJsonVt * -_nm_json_vt_init (void) -{ - NMJsonVtInternal *v; - -again: - v = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); - if (G_UNLIKELY (!v)) { - v = _nm_json_vt_internal_load (); - if (!g_atomic_pointer_compare_and_exchange ((gpointer *) &_nm_json_vt_ptr, NULL, v)) { - if (v->dl_handle) - dlclose (v->dl_handle); - g_free (v); - goto again; - } - - /* we transfer ownership. */ - } - - nm_assert (v && v == g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr)); - return &v->vt; -} - -const NMJsonVt * -nmtst_json_vt_reset (gboolean loaded) -{ - NMJsonVtInternal *v_old; - NMJsonVtInternal *v; - - v_old = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); - - if (!loaded) { - /* load a fake instance for testing. */ - v = g_new0 (NMJsonVtInternal, 1); - } else - v = _nm_json_vt_internal_load (); - - if (!g_atomic_pointer_compare_and_exchange ((gpointer *) &_nm_json_vt_ptr, v_old, v)) - g_assert_not_reached (); - - if (v_old) { - if (v_old->dl_handle) - dlclose (v_old->dl_handle); - g_free ((gpointer *) v_old); - } - - return v->vt.loaded ? &v->vt : NULL; -} diff --git a/shared/nm-glib-aux/nm-json.h b/shared/nm-glib-aux/nm-json.h deleted file mode 100644 index 5042cf0f80..0000000000 --- a/shared/nm-glib-aux/nm-json.h +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2017, 2018 Red Hat, Inc. - */ - -#ifndef __NM_JSON_H__ -#define __NM_JSON_H__ - -/*****************************************************************************/ - -#define NM_JSON_REJECT_DUPLICATES 0x1 - -typedef enum { - NM_JSON_OBJECT, - NM_JSON_ARRAY, - NM_JSON_STRING, - NM_JSON_INTEGER, - NM_JSON_REAL, - NM_JSON_TRUE, - NM_JSON_FALSE, - NM_JSON_NULL, -} nm_json_type; - -typedef struct nm_json_t { - nm_json_type type; - volatile size_t refcount; -} nm_json_t; - -typedef long long nm_json_int_t; - -#define NM_JSON_ERROR_TEXT_LENGTH 160 -#define NM_JSON_ERROR_SOURCE_LENGTH 80 - -typedef struct nm_json_error_t { - int line; - int column; - int position; - char source[NM_JSON_ERROR_SOURCE_LENGTH]; - char text[NM_JSON_ERROR_TEXT_LENGTH]; -} nm_json_error_t; - -typedef struct { - gboolean loaded; - char *(*nm_json_dumps) (const nm_json_t *json, size_t flags); - const char *(*nm_json_object_iter_key) (void *iter); - const char *(*nm_json_string_value) (const nm_json_t *json); - int (*nm_json_array_append_new) (nm_json_t *json, nm_json_t *value); - int (*nm_json_object_del) (nm_json_t *json, const char *key); - int (*nm_json_object_set_new) (nm_json_t *json, const char *key, nm_json_t *value); - nm_json_int_t (*nm_json_integer_value) (const nm_json_t *json); - nm_json_t *(*nm_json_array) (void); - nm_json_t *(*nm_json_array_get) (const nm_json_t *json, size_t index); - nm_json_t *(*nm_json_false) (void); - nm_json_t *(*nm_json_integer) (nm_json_int_t value); - nm_json_t *(*nm_json_loads) (const char *string, size_t flags, nm_json_error_t *error); - nm_json_t *(*nm_json_object) (void); - nm_json_t *(*nm_json_object_get) (const nm_json_t *json, const char *key); - nm_json_t *(*nm_json_object_iter_value) (void *); - nm_json_t *(*nm_json_string) (const char *value); - nm_json_t *(*nm_json_true) (void); - size_t (*nm_json_array_size) (const nm_json_t *json); - size_t (*nm_json_object_size) (const nm_json_t *json); - void (*nm_json_delete) (nm_json_t *json); - void *(*nm_json_object_iter) (nm_json_t *json); - void *(*nm_json_object_iter_next) (nm_json_t *json, void *iter); - void *(*nm_json_object_key_to_iter) (const char *key); -} NMJsonVt; - -extern const NMJsonVt *_nm_json_vt_ptr; - -const NMJsonVt *_nm_json_vt_init (void); - -static inline const NMJsonVt * -_nm_json_vt (void) -{ - const NMJsonVt *vt; - - vt = g_atomic_pointer_get ((gpointer *) &_nm_json_vt_ptr); - if (G_UNLIKELY (!vt)) { - vt = _nm_json_vt_init (); - nm_assert (vt); - } - return vt; -} - -static inline const NMJsonVt * -nm_json_vt (void) -{ - const NMJsonVt *vt; - - vt = _nm_json_vt(); - return vt->loaded ? vt : NULL; -} - -static inline const NMJsonVt * -nm_json_vt_assert (void) -{ - const NMJsonVt *vt; - - vt = _nm_json_vt(); - nm_assert (vt->loaded); - return vt; -} - -const NMJsonVt *nmtst_json_vt_reset (gboolean loaded); - -/*****************************************************************************/ - -#define nm_json_boolean(vt, val) \ - ((val) ? (vt)->nm_json_true () : (vt)->nm_json_false ()) - -static inline void -nm_json_decref (const NMJsonVt *vt, nm_json_t *json) -{ - /* Our ref-counting is not threadsafe, unlike libjansson's. But we never - * share one json_t instance between threads, and if we would, we would very likely - * wrap a mutex around it. */ - if ( json - && json->refcount != (size_t) -1 - && --json->refcount == 0) - vt->nm_json_delete (json); -} - -static inline void -_nm_auto_decref_json (nm_json_t **p_json) -{ - if ( *p_json - && (*p_json)->refcount != (size_t) -1 - && --(*p_json)->refcount == 0) - nm_json_vt ()->nm_json_delete (*p_json); -} - -#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) - -/*****************************************************************************/ - -/* the following are implemented as pure macros in jansson.h. - * They can be used directly, however, add a nm_json* variant, - * to make it explict we don't accidentally use jansson ABI. */ - -#define nm_json_typeof(json) ((json)->type) -#define nm_json_is_object(json) ((json) && nm_json_typeof(json) == NM_JSON_OBJECT) -#define nm_json_is_array(json) ((json) && nm_json_typeof(json) == NM_JSON_ARRAY) -#define nm_json_is_string(json) ((json) && nm_json_typeof(json) == NM_JSON_STRING) -#define nm_json_is_integer(json) ((json) && nm_json_typeof(json) == NM_JSON_INTEGER) -#define nm_json_is_real(json) ((json) && nm_json_typeof(json) == NM_JSON_REAL) -#define nm_json_is_number(json) (nm_json_is_integer(json) || nm_json_is_real(json)) -#define nm_json_is_true(json) ((json) && nm_json_typeof(json) == NM_JSON_TRUE) -#define nm_json_is_false(json) ((json) && nm_json_typeof(json) == NM_JSON_FALSE) -#define nm_json_boolean_value nm_json_is_true -#define nm_json_is_boolean(json) (nm_json_is_true(json) || nm_json_is_false(json)) -#define nm_json_is_null(json) ((json) && nm_json_typeof(json) == NM_JSON_NULL) - -#define nm_json_object_foreach(vt, object, key, value) \ - for(key = vt->nm_json_object_iter_key (vt->nm_json_object_iter (object)); \ - key && (value = vt->nm_json_object_iter_value (vt->nm_json_object_key_to_iter (key))); \ - key = vt->nm_json_object_iter_key (vt->nm_json_object_iter_next (object, vt->nm_json_object_key_to_iter (key)))) - -/*****************************************************************************/ - -static inline int -nm_jansson_json_as_bool (const nm_json_t *elem, - bool *out_val) -{ - if (!elem) - return 0; - - if (!nm_json_is_boolean (elem)) - return -EINVAL; - - NM_SET_OUT (out_val, nm_json_boolean_value (elem)); - return 1; -} - -static inline int -nm_jansson_json_as_int32 (const NMJsonVt *vt, - const nm_json_t *elem, - gint32 *out_val) -{ - nm_json_int_t v; - - if (!elem) - return 0; - - if (!nm_json_is_integer (elem)) - return -EINVAL; - - v = vt->nm_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 NMJsonVt *vt, - const nm_json_t *elem, - int *out_val) -{ - nm_json_int_t v; - - if (!elem) - return 0; - - if (!nm_json_is_integer (elem)) - return -EINVAL; - - v = vt->nm_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 NMJsonVt *vt, - const nm_json_t *elem, - const char **out_val) -{ - if (!elem) - return 0; - - if (!nm_json_is_string (elem)) - return -EINVAL; - - NM_SET_OUT (out_val, vt->nm_json_string_value (elem)); - return 1; -} - -/*****************************************************************************/ - -#ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS - -#include "nm-glib-aux/nm-value-type.h" - -static inline gboolean -nm_value_type_from_json (const NMJsonVt *vt, - NMValueType value_type, - const nm_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 (vt, elem, out_val) > 0); - case NM_VALUE_TYPE_INT: return (nm_jansson_json_as_int (vt, 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 (vt, elem, out_val) > 0); - - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached (); - return FALSE; -} - -#endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ - -#endif /* __NM_JSON_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index b8fdd922ce..935ea9fdf8 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -15,7 +15,7 @@ #include #include -#include "nm-glib-aux/nm-json.h" +#include "nm-glib-aux/nm-json-aux.h" #include "nm-utils.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" From 57de0c27a77d7ba3a7d08d359d1f4124bab22afb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 20:12:25 +0200 Subject: [PATCH 09/13] shared,libnm: rename nm_json_aux_gstr_*() API to nm_json_gstr_*() --- libnm-core/nm-team-utils.c | 32 +++++++++++++++--------------- shared/nm-glib-aux/nm-json-aux.c | 18 ++++++++--------- shared/nm-glib-aux/nm-json-aux.h | 34 ++++++++++++++++---------------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index ccdefecbf7..ca18c7d6a0 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -459,9 +459,9 @@ _team_attr_data_to_json (const TeamAttrData *attr_data, _team_attr_data_ASSERT (attr_data); nm_assert (p_field); - nm_json_aux_gstr_append_obj_name (gstr, - attr_data->js_keys[attr_data->js_keys_len - 1], - '\0'); + nm_json_gstr_append_obj_name (gstr, + attr_data->js_keys[attr_data->js_keys_len - 1], + '\0'); if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) { nm_value_type_to_json (attr_data->value_type, gstr, p_field); @@ -481,7 +481,7 @@ _team_attr_data_to_json (const TeamAttrData *attr_data, g_string_append (gstr, "[ "); for (i = 0; i < v_ptrarray->len; i++) { if (i > 0) - nm_json_aux_gstr_append_delimiter (gstr); + nm_json_gstr_append_delimiter (gstr); _link_watcher_to_json (v_ptrarray->pdata[i], gstr); } g_string_append (gstr, " ]"); @@ -499,8 +499,8 @@ _team_attr_data_to_json (const TeamAttrData *attr_data, g_string_append (gstr, "[ "); for (i = 0; i < v_ptrarray->len; i++) { if (i > 0) - nm_json_aux_gstr_append_delimiter (gstr); - nm_json_aux_gstr_append_string (gstr, v_ptrarray->pdata[i]); + nm_json_gstr_append_delimiter (gstr); + nm_json_gstr_append_string (gstr, v_ptrarray->pdata[i]); } g_string_append (gstr, i > 0 ? " ]" : "]"); } @@ -670,7 +670,7 @@ _team_setting_field_to_json (const NMTeamSetting *self, return FALSE; if (prepend_delimiter) - nm_json_aux_gstr_append_delimiter (gstr); + nm_json_gstr_append_delimiter (gstr); _team_attr_data_to_json (attr_data, self->d.is_port, gstr, @@ -1107,8 +1107,8 @@ _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher, if (is_first) is_first = FALSE; else - nm_json_aux_gstr_append_delimiter (gstr); - nm_json_aux_gstr_append_obj_name (gstr, attr_data->js_key, '\0'); + nm_json_gstr_append_delimiter (gstr); + nm_json_gstr_append_obj_name (gstr, attr_data->js_key, '\0'); nm_value_type_to_json (attr_data->value_type, gstr, &p_val->val); } @@ -1573,15 +1573,15 @@ nm_team_setting_config_get (const NMTeamSetting *self) nm_assert (list_is_empty); - nm_json_aux_gstr_append_obj_name (gstr, "runner", '{'); + nm_json_gstr_append_obj_name (gstr, "runner", '{'); if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty2, attr_lst_runner_pt1, G_N_ELEMENTS (attr_lst_runner_pt1))) list_is_empty2 = FALSE; if (_team_setting_has_fields_any_v (self, attr_lst_runner_pt2, G_N_ELEMENTS (attr_lst_runner_pt2))) { if (!list_is_empty2) - nm_json_aux_gstr_append_delimiter (gstr); - nm_json_aux_gstr_append_obj_name (gstr, "tx_balancer", '{'); + nm_json_gstr_append_delimiter (gstr); + nm_json_gstr_append_obj_name (gstr, "tx_balancer", '{'); if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, attr_lst_runner_pt2, G_N_ELEMENTS (attr_lst_runner_pt2))) nm_assert_not_reached (); g_string_append (gstr, " }"); @@ -1598,8 +1598,8 @@ nm_team_setting_config_get (const NMTeamSetting *self) if (_team_setting_has_fields_any_v (self, attr_lst_notify_peers, G_N_ELEMENTS (attr_lst_notify_peers))) { if (!list_is_empty) - nm_json_aux_gstr_append_delimiter (gstr); - nm_json_aux_gstr_append_obj_name (gstr, "notify_peers", '{'); + nm_json_gstr_append_delimiter (gstr); + nm_json_gstr_append_obj_name (gstr, "notify_peers", '{'); if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, attr_lst_notify_peers, G_N_ELEMENTS (attr_lst_notify_peers))) nm_assert_not_reached (); g_string_append (gstr, " }"); @@ -1608,8 +1608,8 @@ nm_team_setting_config_get (const NMTeamSetting *self) if (_team_setting_has_fields_any_v (self, attr_lst_mcast_rejoin, G_N_ELEMENTS (attr_lst_mcast_rejoin))) { if (!list_is_empty) - nm_json_aux_gstr_append_delimiter (gstr); - nm_json_aux_gstr_append_obj_name (gstr, "mcast_rejoin", '{'); + nm_json_gstr_append_delimiter (gstr); + nm_json_gstr_append_obj_name (gstr, "mcast_rejoin", '{'); if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, attr_lst_mcast_rejoin, G_N_ELEMENTS (attr_lst_mcast_rejoin))) nm_assert_not_reached (); g_string_append (gstr, " }"); diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c index 17a70edf75..6844ebcbfb 100644 --- a/shared/nm-glib-aux/nm-json-aux.c +++ b/shared/nm-glib-aux/nm-json-aux.c @@ -98,9 +98,9 @@ _gstr_append_string_len (GString *gstr, } void -nm_json_aux_gstr_append_string_len (GString *gstr, - const char *str, - gsize n) +nm_json_gstr_append_string_len (GString *gstr, + const char *str, + gsize n) { g_return_if_fail (gstr); @@ -108,8 +108,8 @@ nm_json_aux_gstr_append_string_len (GString *gstr, } void -nm_json_aux_gstr_append_string (GString *gstr, - const char *str) +nm_json_gstr_append_string (GString *gstr, + const char *str) { g_return_if_fail (gstr); @@ -120,14 +120,14 @@ nm_json_aux_gstr_append_string (GString *gstr, } void -nm_json_aux_gstr_append_obj_name (GString *gstr, - const char *key, - char start_container) +nm_json_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); + nm_json_gstr_append_string (gstr, key); if (start_container != '\0') { nm_assert (NM_IN_SET (start_container, '[', '{')); diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h index def96b6a7c..082b9e5205 100644 --- a/shared/nm-glib-aux/nm-json-aux.h +++ b/shared/nm-glib-aux/nm-json-aux.h @@ -11,36 +11,36 @@ /*****************************************************************************/ static inline GString * -nm_json_aux_gstr_append_delimiter (GString *gstr) +nm_json_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_gstr_append_string_len (GString *gstr, + const char *str, + gsize n); -void nm_json_aux_gstr_append_string (GString *gstr, - const char *str); +void nm_json_gstr_append_string (GString *gstr, + const char *str); static inline void -nm_json_aux_gstr_append_bool (GString *gstr, - gboolean v) +nm_json_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) +nm_json_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); +void nm_json_gstr_append_obj_name (GString *gstr, + const char *key, + char start_container); /*****************************************************************************/ @@ -285,10 +285,10 @@ nm_value_type_to_json (NMValueType value_type, 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_BOOL: nm_json_gstr_append_bool (gstr, *((const bool *) p_field)); return; + case NM_VALUE_TYPE_INT32: nm_json_gstr_append_int64 (gstr, *((const gint32 *) p_field)); return; + case NM_VALUE_TYPE_INT: nm_json_gstr_append_int64 (gstr, *((const int *) p_field)); return; + case NM_VALUE_TYPE_STRING: nm_json_gstr_append_string (gstr, *((const char *const *) p_field)); return; case NM_VALUE_TYPE_UNSPEC: break; } From ae5e8fc26ba8990832301791092b6fd1aefe9afc Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 20:19:52 +0200 Subject: [PATCH 10/13] shared/tests: add test for checking "nm-json-aux.h" Our "nm-json-aux.h" redefines various things from header. Add a unit test that checks that what we redefine exactly matches what libjansson would provide, so that they are compatible. --- .gitignore | 1 + Makefile.am | 33 +++++ shared/nm-glib-aux/tests/meson.build | 42 ++++-- shared/nm-glib-aux/tests/test-json-aux.c | 163 +++++++++++++++++++++++ 4 files changed, 228 insertions(+), 11 deletions(-) create mode 100644 shared/nm-glib-aux/tests/test-json-aux.c diff --git a/.gitignore b/.gitignore index 5cb43a0374..919b1830c9 100644 --- a/.gitignore +++ b/.gitignore @@ -131,6 +131,7 @@ test-*.trs /examples/C/qt/monitor-nm-running /examples/C/qt/monitor-nm-running.moc +/shared/nm-glib-aux/tests/test-json-aux /shared/nm-glib-aux/tests/test-shared-general /shared/nm-version-macros.h diff --git a/Makefile.am b/Makefile.am index ad371c4247..01fd835e17 100644 --- a/Makefile.am +++ b/Makefile.am @@ -642,6 +642,39 @@ shared_nm_glib_aux_tests_test_shared_general_LDADD = \ ############################################################################### +if WITH_JANSSON + +check_programs += shared/nm-glib-aux/tests/test-json-aux + +shared_nm_glib_aux_tests_test_json_aux_CPPFLAGS = \ + $(dflt_cppflags) \ + -I$(srcdir)/shared \ + -DNETWORKMANAGER_COMPILATION_TEST \ + -DNETWORKMANAGER_COMPILATION='(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)' \ + $(CODE_COVERAGE_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(JANSSON_CFLAGS) \ + $(SANITIZER_LIB_CFLAGS) \ + $(NULL) + +shared_nm_glib_aux_tests_test_json_aux_LDFLAGS = \ + $(CODE_COVERAGE_LDFLAGS) \ + $(SANITIZER_EXEC_LDFLAGS) \ + $(NULL) + +shared_nm_glib_aux_tests_test_json_aux_LDADD = \ + $(JANSSON_LIBS) \ + shared/nm-glib-aux/libnm-glib-aux.la \ + shared/systemd/libnm-systemd-logging-stub.la \ + shared/nm-std-aux/libnm-std-aux.la \ + shared/libcsiphash.la \ + $(GLIB_LIBS) \ + $(NULL) + +endif + +############################################################################### + noinst_LTLIBRARIES += introspection/libnmdbus.la introspection_libnmdbus_la_CPPFLAGS = $(GLIB_CFLAGS) diff --git a/shared/nm-glib-aux/tests/meson.build b/shared/nm-glib-aux/tests/meson.build index f6328db916..8136eff935 100644 --- a/shared/nm-glib-aux/tests/meson.build +++ b/shared/nm-glib-aux/tests/meson.build @@ -1,23 +1,43 @@ # SPDX-License-Identifier: LGPL-2.1+ -test_unit = 'test-shared-general' - -c_flags = [ - '-DNETWORKMANAGER_COMPILATION_TEST', - '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', -] - exe = executable( - test_unit, - test_unit + '.c', - c_args: c_flags, + 'test-shared-general', + 'test-shared-general.c', + c_args: [ + '-DNETWORKMANAGER_COMPILATION_TEST', + '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', + ], dependencies: libnm_utils_base_dep, link_with: libnm_systemd_logging_stub, ) test( - 'shared/nm-glib-aux/' + test_unit, + 'shared/nm-glib-aux/test-shared-general', test_script, args: test_args + [exe.full_path()], timeout: default_test_timeout, ) + +if jansson_dep.found() + exe = executable( + 'test-json-aux', + 'test-json-aux.c', + c_args: [ + '-DNETWORKMANAGER_COMPILATION_TEST', + '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', + ], + dependencies: [ + libnm_utils_base_dep, + jansson_dep, + dl_dep, + ], + link_with: libnm_systemd_logging_stub, + ) + + test( + 'shared/nm-glib-aux/test-json-aux', + test_script, + args: test_args + [exe.full_path()], + timeout: default_test_timeout, + ) +endif diff --git a/shared/nm-glib-aux/tests/test-json-aux.c b/shared/nm-glib-aux/tests/test-json-aux.c new file mode 100644 index 0000000000..cde1bef3b0 --- /dev/null +++ b/shared/nm-glib-aux/tests/test-json-aux.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#define NM_TEST_UTILS_NO_LIBNM 1 + +#include "nm-default.h" + +#include + +#include "nm-glib-aux/nm-json-aux.h" + +#include "nm-utils/nm-test-utils.h" + +/*****************************************************************************/ + +static void +test_jansson (void) +{ + const NMJsonVt *vt; + nm_auto_decref_json nm_json_t *js1 = NULL; + nm_auto_decref_json nm_json_t *js2 = NULL; + +#define _ASSERT_FIELD(type1, type2, field) \ + G_STMT_START { \ + G_STATIC_ASSERT_EXPR (sizeof (((type1 *) NULL)->field) == sizeof (((type2 *) NULL)->field)); \ + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (type1, field) == G_STRUCT_OFFSET (type2, field)); \ + } G_STMT_END + + G_STATIC_ASSERT_EXPR (NM_JSON_REJECT_DUPLICATES == JSON_REJECT_DUPLICATES); + + G_STATIC_ASSERT_EXPR (sizeof (nm_json_type) == sizeof (json_type)); + + G_STATIC_ASSERT_EXPR ((int) NM_JSON_OBJECT == JSON_OBJECT); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_ARRAY == JSON_ARRAY); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_STRING == JSON_STRING); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_INTEGER == JSON_INTEGER); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_REAL == JSON_REAL); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_TRUE == JSON_TRUE); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_FALSE == JSON_FALSE); + G_STATIC_ASSERT_EXPR ((int) NM_JSON_NULL == JSON_NULL); + + G_STATIC_ASSERT_EXPR (sizeof (nm_json_int_t) == sizeof (json_int_t)); + + G_STATIC_ASSERT_EXPR (sizeof (nm_json_t) == sizeof (json_t)); + _ASSERT_FIELD (nm_json_t, json_t, refcount); + _ASSERT_FIELD (nm_json_t, json_t, type); + + G_STATIC_ASSERT_EXPR (NM_JSON_ERROR_TEXT_LENGTH == JSON_ERROR_TEXT_LENGTH); + G_STATIC_ASSERT_EXPR (NM_JSON_ERROR_SOURCE_LENGTH == JSON_ERROR_SOURCE_LENGTH); + + G_STATIC_ASSERT_EXPR (sizeof (nm_json_error_t) == sizeof (json_error_t)); + _ASSERT_FIELD (nm_json_error_t, json_error_t, line); + _ASSERT_FIELD (nm_json_error_t, json_error_t, column); + _ASSERT_FIELD (nm_json_error_t, json_error_t, position); + _ASSERT_FIELD (nm_json_error_t, json_error_t, source); + _ASSERT_FIELD (nm_json_error_t, json_error_t, text); + + vt = nm_json_vt (); + + g_assert (vt); + g_assert (vt->loaded); + + js1 = vt->nm_json_loads ("{ \"a\": 5 }", 0, NULL); + g_assert (js1); + nm_json_decref (vt, g_steal_pointer (&js1)); + + js2 = vt->nm_json_loads ("{ \"a\": 6 }", 0, NULL); + g_assert (js2); + +#define CHECK_FCN(vt, fcn, nm_type, js_type) \ + G_STMT_START { \ + const NMJsonVt *const _vt = (vt); \ + _nm_unused nm_type = (_vt->nm_##fcn); \ + _nm_unused js_type = (fcn); \ + \ + g_assert (_vt->nm_##fcn); \ + g_assert (_f_nm); \ + g_assert (_f_js); \ + g_assert (_f_nm == _vt->nm_##fcn); \ + } G_STMT_END + + CHECK_FCN (vt, json_array, + nm_json_t *(*_f_nm) (void), + json_t *(*_f_js) (void)); + CHECK_FCN (vt, json_array_append_new, + int (*_f_nm) (nm_json_t *, nm_json_t *), + int (*_f_js) ( json_t *, json_t *)); + CHECK_FCN (vt, json_array_get, + nm_json_t *(*_f_nm) (const nm_json_t *, gsize ), + json_t *(*_f_js) (const json_t *, size_t)); + CHECK_FCN (vt, json_array_size, + gsize (*_f_nm) (const nm_json_t *), + size_t (*_f_js) (const json_t *)); + CHECK_FCN (vt, json_delete, + void (*_f_nm) (nm_json_t *), + void (*_f_js) ( json_t *)); + CHECK_FCN (vt, json_dumps, + char *(*_f_nm) (const nm_json_t *, gsize ), + char *(*_f_js) (const json_t *, size_t)); + CHECK_FCN (vt, json_false, + nm_json_t *(*_f_nm) (void), + json_t *(*_f_js) (void)); + CHECK_FCN (vt, json_integer, + nm_json_t *(*_f_nm) (nm_json_int_t), + json_t *(*_f_js) ( json_int_t)); + CHECK_FCN (vt, json_integer_value, + nm_json_int_t (*_f_nm) (const nm_json_t *), + json_int_t (*_f_js) (const json_t *)); + CHECK_FCN (vt, json_loads, + nm_json_t *(*_f_nm) (const char *, gsize , nm_json_error_t *), + json_t *(*_f_js) (const char *, size_t, json_error_t *)); + CHECK_FCN (vt, json_object, + nm_json_t *(*_f_nm) (void), + json_t *(*_f_js) (void)); + CHECK_FCN (vt, json_object_del, + int (*_f_nm) (nm_json_t *, const char *), + int (*_f_js) ( json_t *, const char *)); + CHECK_FCN (vt, json_object_get, + nm_json_t *(*_f_nm) (const nm_json_t *, const char *), + json_t *(*_f_js) (const json_t *, const char *)); + CHECK_FCN (vt, json_object_iter, + void *(*_f_nm) (nm_json_t *), + void *(*_f_js) ( json_t *)); + CHECK_FCN (vt, json_object_iter_key, + const char *(*_f_nm) (void *), + const char *(*_f_js) (void *)); + CHECK_FCN (vt, json_object_iter_next, + void *(*_f_nm) (nm_json_t *, void *), + void *(*_f_js) ( json_t *, void *)); + CHECK_FCN (vt, json_object_iter_value, + nm_json_t *(*_f_nm) (void *), + json_t *(*_f_js) (void *)); + CHECK_FCN (vt, json_object_key_to_iter, + void *(*_f_nm) (const char *), + void *(*_f_js) (const char *)); + CHECK_FCN (vt, json_object_set_new, + int (*_f_nm) (nm_json_t *, const char *, nm_json_t *), + int (*_f_js) ( json_t *, const char *, json_t *)); + CHECK_FCN (vt, json_object_size, + gsize (*_f_nm) (const nm_json_t *), + size_t (*_f_js) (const json_t *)); + CHECK_FCN (vt, json_string, + nm_json_t *(*_f_nm) (const char *), + json_t *(*_f_js) (const char *)); + CHECK_FCN (vt, json_string_value, + const char *(*_f_nm) (const nm_json_t *), + const char *(*_f_js) (const json_t *)); + CHECK_FCN (vt, json_true, + nm_json_t *(*_f_nm) (void), + json_t *(*_f_js) (void)); +} + +/*****************************************************************************/ + +NMTST_DEFINE (); + +int main (int argc, char **argv) +{ + nmtst_init (&argc, &argv, TRUE); + + g_test_add_func ("/general/test_jansson", test_jansson); + + return g_test_run (); +} From 7054b113284c64cca6f709d887b54a58678e401e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 23:39:53 +0200 Subject: [PATCH 11/13] all: bump libjansson dependency to 2.7 jansson 2.7 was released October 2014. It's also in Ubuntu 16.06. Other distros (like CentOS 7.5 and Debian Stretch/9) have both newer versions. Bump the requirement, simply because our CI does not use such old version so it's not clear whether it works at all. --- configure.ac | 2 +- meson.build | 2 +- shared/nm-glib-aux/nm-jansson.h | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 4ea4dbf081..86f91e21fe 100644 --- a/configure.ac +++ b/configure.ac @@ -583,7 +583,7 @@ PKG_CHECK_MODULES(UUID, uuid) # Teamd control checks -PKG_CHECK_MODULES(JANSSON, [jansson >= 2.5], [have_jansson=yes], [have_jansson=no]) +PKG_CHECK_MODULES(JANSSON, [jansson >= 2.7], [have_jansson=yes], [have_jansson=no]) if test "$have_jansson" = "yes"; then AC_DEFINE(WITH_JANSSON, 1, [Define if JANSSON is enabled]) diff --git a/meson.build b/meson.build index 28cd4f3028..f8973b4680 100644 --- a/meson.build +++ b/meson.build @@ -253,7 +253,7 @@ libudev_dep = dependency('libudev', version: '>= 175') dbus_dep = dependency('dbus-1', version: '>= 1.1') libndp_dep = dependency('libndp') -jansson_dep = dependency('jansson', version: '>= 2.5', required: false) +jansson_dep = dependency('jansson', version: '>= 2.7', required: false) config_h.set10('WITH_JANSSON', jansson_dep.found()) jansson_msg = 'no' diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h index 7c034222c4..946f42d183 100644 --- a/shared/nm-glib-aux/nm-jansson.h +++ b/shared/nm-glib-aux/nm-jansson.h @@ -12,11 +12,6 @@ #include -/* Added in Jansson v2.7 */ -#ifndef json_boolean_value -#define json_boolean_value json_is_true -#endif - /* Added in Jansson v2.8 */ #ifndef json_object_foreach_safe #define json_object_foreach_safe(object, n, key, value) \ From 18692faa9fddf2a1208ad593195d794710eeea03 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 1 Jul 2020 23:39:53 +0200 Subject: [PATCH 12/13] shared: drop unused code from "shared/nm-glib-aux/nm-jansson.h" --- shared/nm-glib-aux/nm-jansson.h | 100 -------------------------------- 1 file changed, 100 deletions(-) diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h index 946f42d183..9314b50c69 100644 --- a/shared/nm-glib-aux/nm-jansson.h +++ b/shared/nm-glib-aux/nm-jansson.h @@ -25,106 +25,6 @@ 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__ */ From ca7bb15591bb585156bcf17aa3c43817605b05ee Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jul 2020 20:13:41 +0200 Subject: [PATCH 13/13] shared: cleanup dlopening libjansson depending on configure options - assert that WITH_JANSSON and JANSSON_SONAME are defined consistently. This check ensures that we can check at compile time that nm_json_vt() will always fail (because JANSSON_SONAME) is undefined. That is interesting, because this means you can do a compile time for !WITH_JANSSON, and know if nm_json_vt() will *never* succeed. With that, we could let the linker know when the code is unused and remove all uses of nm_json_vt(), without using the traditional conditional compilation with "#if WITH_JANSSON". But of course, we currently don't do this micro optimization to remove defunct code. - drop the "mode" helper variable and pass the flags directly to dlopen(). --- libnm-core/tests/test-setting.c | 19 +++++++++++++++++++ shared/nm-glib-aux/nm-json-aux.c | 31 +++++++++++++++++++------------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 5ec0ccbdb1..1b53baeda3 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -970,6 +970,24 @@ test_dcb_bandwidth_sums (void) /*****************************************************************************/ +static void +test_nm_json (void) +{ + g_assert (NM_IN_SET (WITH_JANSSON, 0, 1)); + +#if WITH_JANSSON + g_assert (nm_json_vt ()); +#else + g_assert (!nm_json_vt ()); +#endif + +#if WITH_JANSSON != defined (JANSSON_SONAME) +#error "WITH_JANSON and JANSSON_SONAME are defined inconsistently." +#endif +} + +/*****************************************************************************/ + static void _test_team_config_sync (const char *team_config, int notify_peer_count, @@ -4087,6 +4105,7 @@ main (int argc, char **argv) g_test_add_func ("/libnm/settings/bridge/vlans", test_bridge_vlans); g_test_add_func ("/libnm/settings/bridge/verify", test_bridge_verify); + g_test_add_func ("/libnm/test_nm_json", test_nm_json); g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin", test_runner_roundrobin_sync_from_config); g_test_add_func ("/libnm/settings/team/sync_runner_from_config_broadcast", diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c index 6844ebcbfb..e9816d7781 100644 --- a/shared/nm-glib-aux/nm-json-aux.c +++ b/shared/nm-glib-aux/nm-json-aux.c @@ -147,24 +147,31 @@ static NMJsonVtInternal * _nm_json_vt_internal_load (void) { NMJsonVtInternal *v; - void *handle = NULL; - int mode; + const char *soname; + void *handle; v = g_new0 (NMJsonVtInternal, 1); -#ifndef JANSSON_SONAME -#define JANSSON_SONAME "" +#if WITH_JANSSON && defined (JANSSON_SONAME) + G_STATIC_ASSERT_EXPR (NM_STRLEN (JANSSON_SONAME) > 0); + nm_assert (strlen (JANSSON_SONAME) > 0); + soname = JANSSON_SONAME; +#elif !WITH_JANSSON && !defined (JANSSON_SONAME) + soname = NULL; +#else +#error "WITH_JANSON and JANSSON_SONAME are defined inconsistently." #endif - mode = RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND; -#if defined (ASAN_BUILD) - /* Address sanitizer is incompatible with RTLD_DEEPBIND. */ - mode &= ~RTLD_DEEPBIND; + if (!soname) + return v; + + handle = dlopen (soname, RTLD_LAZY + | RTLD_LOCAL + | RTLD_NODELETE +#if !defined (ASAN_BUILD) + | RTLD_DEEPBIND #endif - - if (strlen (JANSSON_SONAME) > 0) - handle = dlopen (JANSSON_SONAME, mode); - + | 0); if (!handle) return v;