mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-06 16:21:50 +00:00
d38b5d92ee
[thaller@redhat.com: original patch modified]
287 lines
7.8 KiB
C
287 lines
7.8 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
/*
|
|
* Copyright (C) 2017 - 2019 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.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;
|
|
}
|