diff options
Diffstat (limited to 'src/libnm-glib-aux/nm-json-aux.c')
-rw-r--r-- | src/libnm-glib-aux/nm-json-aux.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/src/libnm-glib-aux/nm-json-aux.c b/src/libnm-glib-aux/nm-json-aux.c new file mode 100644 index 0000000000..dc67d6d593 --- /dev/null +++ b/src/libnm-glib-aux/nm-json-aux.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2019 Red Hat, Inc. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-json-aux.h" + +#include <dlfcn.h> + +/*****************************************************************************/ + +/* If RTLD_DEEPBIND isn't available just ignore it. This can cause problems + * with jansson, json-glib, and cjson symbols clashing (and as such crashing the + * program). But that needs to be fixed by the json libraries, and it is by adding + * symbol versioning in recent versions. */ +#ifndef RTLD_DEEPBIND + #define RTLD_DEEPBIND 0 +#endif + +/*****************************************************************************/ + +static void +_gstr_append_string_len(GString *gstr, const char *str, gsize len) +{ + g_string_append_c(gstr, '\"'); + + while (len > 0) { + gsize n; + const char *end; + gboolean valid; + + nm_assert(len > 0); + + valid = g_utf8_validate(str, len, &end); + + nm_assert(end && end >= str && end <= &str[len]); + + if (end > str) { + const char *s; + + for (s = str; s < end; s++) { + nm_assert(s[0] != '\0'); + + if (s[0] < 0x20) { + const char *text; + + switch (s[0]) { + case '\\': + text = "\\\\"; + break; + case '\"': + text = "\\\""; + break; + case '\b': + text = "\\b"; + break; + case '\f': + text = "\\f"; + break; + case '\n': + text = "\\n"; + break; + case '\r': + text = "\\r"; + break; + case '\t': + text = "\\t"; + break; + default: + g_string_append_printf(gstr, "\\u%04X", (guint) s[0]); + continue; + } + g_string_append(gstr, text); + continue; + } + + if (NM_IN_SET(s[0], '\\', '\"')) + g_string_append_c(gstr, '\\'); + g_string_append_c(gstr, s[0]); + } + } else + nm_assert(!valid); + + if (valid) { + nm_assert(end == &str[len]); + break; + } + + nm_assert(end < &str[len]); + + if (end[0] == '\0') { + /* there is a NUL byte in the string. Technically this is valid UTF-8, so we + * encode it there. However, this will likely result in a truncated string when + * parsing. */ + g_string_append(gstr, "\\u0000"); + } else { + /* the character is not valid UTF-8. There is nothing we can do about it, because + * JSON can only contain UTF-8 and even the escape sequences can only escape Unicode + * codepoints (but not binary). + * + * The argument is not a string (in any known encoding), hence we cannot represent + * it as a JSON string (which are unicode strings). + * + * Print an underscore instead of the invalid char :) */ + g_string_append_c(gstr, '_'); + } + + n = str - end; + nm_assert(n < len); + n++; + str += n; + len -= n; + } + + g_string_append_c(gstr, '\"'); +} + +void +nm_json_gstr_append_string_len(GString *gstr, const char *str, gsize n) +{ + g_return_if_fail(gstr); + + _gstr_append_string_len(gstr, str, n); +} + +void +nm_json_gstr_append_string(GString *gstr, const char *str) +{ + g_return_if_fail(gstr); + + if (!str) + g_string_append(gstr, "null"); + else + _gstr_append_string_len(gstr, str, strlen(str)); +} + +void +nm_json_gstr_append_obj_name(GString *gstr, const char *key, char start_container) +{ + g_return_if_fail(gstr); + g_return_if_fail(key); + + nm_json_gstr_append_string(gstr, key); + + if (start_container != '\0') { + nm_assert(NM_IN_SET(start_container, '[', '{')); + g_string_append_printf(gstr, ": %c ", start_container); + } else + g_string_append(gstr, ": "); +} + +/*****************************************************************************/ + +typedef struct { + NMJsonVt vt; + void * dl_handle; +} NMJsonVtInternal; + +static NMJsonVtInternal * +_nm_json_vt_internal_load(void) +{ + NMJsonVtInternal *v; + const char * soname; + void * handle; + + v = g_new0(NMJsonVtInternal, 1); + +#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 + + if (!soname) + return v; + + handle = dlopen(soname, + RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE +#if !defined(ASAN_BUILD) + | RTLD_DEEPBIND +#endif + | 0); + 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; +} |