mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-01 12:38:04 +02:00
- 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().
270 lines
6.2 KiB
C
270 lines
6.2 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>
|
|
|
|
/*****************************************************************************/
|
|
|
|
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;
|
|
}
|