]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
cd0b6c53 LP |
2 | |
3 | #include <errno.h> | |
ca78ad1d | 4 | #include <locale.h> |
cd0b6c53 | 5 | #include <stdarg.h> |
cd0b6c53 | 6 | #include <stdlib.h> |
cd0b6c53 LP |
7 | #include <sys/types.h> |
8 | ||
9 | #include "sd-messages.h" | |
10 | ||
11 | #include "alloc-util.h" | |
2b2fec7d | 12 | #include "errno-util.h" |
84738d86 | 13 | #include "escape.h" |
cd0b6c53 LP |
14 | #include "fd-util.h" |
15 | #include "fileio.h" | |
16 | #include "float.h" | |
17 | #include "hexdecoct.h" | |
18 | #include "json-internal.h" | |
19 | #include "json.h" | |
20 | #include "macro.h" | |
1561db8a | 21 | #include "math-util.h" |
0a970718 | 22 | #include "memory-util.h" |
2485b7e2 | 23 | #include "memstream-util.h" |
a636a058 | 24 | #include "set.h" |
cd0b6c53 LP |
25 | #include "string-table.h" |
26 | #include "string-util.h" | |
27 | #include "strv.h" | |
28 | #include "terminal-util.h" | |
a42ef715 | 29 | #include "user-util.h" |
cd0b6c53 LP |
30 | #include "utf8.h" |
31 | ||
898820ed | 32 | /* Refuse putting together variants with a larger depth than 2K by default (as a protection against overflowing stacks |
b2fa0d4f | 33 | * if code processes JSON objects recursively. Note that we store the depth in an uint16_t, hence make sure this |
d4a389eb | 34 | * remains under 2^16. |
898820ed LP |
35 | * |
36 | * The value first was 16k, but it was discovered to be too high on llvm/x86-64. See also: | |
37 | * https://github.com/systemd/systemd/issues/10738 | |
38 | * | |
39 | * The value then was 4k, but it was discovered to be too high on s390x/aarch64. See also: | |
40 | * https://github.com/systemd/systemd/issues/14396 */ | |
41 | ||
42 | #define DEPTH_MAX (2U*1024U) | |
b2fa0d4f LP |
43 | assert_cc(DEPTH_MAX <= UINT16_MAX); |
44 | ||
cd0b6c53 LP |
45 | typedef struct JsonSource { |
46 | /* When we parse from a file or similar, encodes the filename, to indicate the source of a json variant */ | |
6dd18b34 | 47 | unsigned n_ref; |
cd0b6c53 LP |
48 | unsigned max_line; |
49 | unsigned max_column; | |
50 | char name[]; | |
51 | } JsonSource; | |
52 | ||
53 | /* On x86-64 this whole structure should have a size of 6 * 64 bit = 48 bytes */ | |
54 | struct JsonVariant { | |
55 | union { | |
56 | /* We either maintain a reference counter for this variant itself, or we are embedded into an | |
57 | * array/object, in which case only that surrounding object is ref-counted. (If 'embedded' is false, | |
58 | * see below.) */ | |
6dd18b34 | 59 | unsigned n_ref; |
cd0b6c53 LP |
60 | |
61 | /* If this JsonVariant is part of an array/object, then this field points to the surrounding | |
62 | * JSON_VARIANT_ARRAY/JSON_VARIANT_OBJECT object. (If 'embedded' is true, see below.) */ | |
63 | JsonVariant *parent; | |
64 | }; | |
65 | ||
a7efb030 LP |
66 | /* If this was parsed from some file or buffer, this stores where from, as well as the source line/column */ |
67 | JsonSource *source; | |
68 | unsigned line, column; | |
69 | ||
4d244632 ZJS |
70 | /* The current 'depth' of the JsonVariant, i.e. how many levels of member variants this has */ |
71 | uint16_t depth; | |
72 | ||
73 | JsonVariantType type:8; | |
cd0b6c53 LP |
74 | |
75 | /* A marker whether this variant is embedded into in array/object or not. If true, the 'parent' pointer above | |
76 | * is valid. If false, the 'n_ref' field above is valid instead. */ | |
77 | bool is_embedded:1; | |
78 | ||
79 | /* In some conditions (for example, if this object is part of an array of strings or objects), we don't store | |
80 | * any data inline, but instead simply reference an external object and act as surrogate of it. In that case | |
81 | * this bool is set, and the external object is referenced through the .reference field below. */ | |
82 | bool is_reference:1; | |
83 | ||
84 | /* While comparing two arrays, we use this for marking what we already have seen */ | |
85 | bool is_marked:1; | |
86 | ||
b7fc90a2 | 87 | /* Erase from memory when freeing */ |
83bc6cb7 LP |
88 | bool sensitive:1; |
89 | ||
b7fc90a2 LP |
90 | /* If this is an object the fields are strictly ordered by name */ |
91 | bool sorted:1; | |
92 | ||
93 | /* If in addition to this object all objects referenced by it are also ordered strictly by name */ | |
94 | bool normalized:1; | |
95 | ||
cd0b6c53 LP |
96 | union { |
97 | /* For simple types we store the value in-line. */ | |
98 | JsonValue value; | |
99 | ||
100 | /* For objects and arrays we store the number of elements immediately following */ | |
101 | size_t n_elements; | |
102 | ||
103 | /* If is_reference as indicated above is set, this is where the reference object is actually stored. */ | |
104 | JsonVariant *reference; | |
105 | ||
f41e4b82 ZJS |
106 | /* Strings are placed immediately after the structure. Note that when this is a JsonVariant |
107 | * embedded into an array we might encode strings up to INLINE_STRING_LENGTH characters | |
108 | * directly inside the element, while longer strings are stored as references. When this | |
109 | * object is not embedded into an array, but stand-alone, we allocate the right size for the | |
110 | * whole structure, i.e. the array might be much larger than INLINE_STRING_LENGTH. */ | |
111 | DECLARE_FLEX_ARRAY(char, string); | |
cd0b6c53 LP |
112 | }; |
113 | }; | |
114 | ||
337712e7 | 115 | /* Inside string arrays we have a series of JsonVariant structures one after the other. In this case, strings longer |
cd0b6c53 | 116 | * than INLINE_STRING_MAX are stored as references, and all shorter ones inline. (This means — on x86-64 — strings up |
337712e7 | 117 | * to 7 chars are stored within the array elements, and all others in separate allocations) */ |
cd0b6c53 LP |
118 | #define INLINE_STRING_MAX (sizeof(JsonVariant) - offsetof(JsonVariant, string) - 1U) |
119 | ||
120 | /* Let's make sure this structure isn't increased in size accidentally. This check is only for our most relevant arch | |
121 | * (x86-64). */ | |
9dc78edc | 122 | #if defined(__x86_64__) && __SIZEOF_POINTER__ == 8 |
337712e7 ZJS |
123 | assert_cc(sizeof(JsonVariant) == 40U); |
124 | assert_cc(INLINE_STRING_MAX == 7U); | |
cd0b6c53 LP |
125 | #endif |
126 | ||
127 | static JsonSource* json_source_new(const char *name) { | |
128 | JsonSource *s; | |
129 | ||
130 | assert(name); | |
131 | ||
132 | s = malloc(offsetof(JsonSource, name) + strlen(name) + 1); | |
133 | if (!s) | |
134 | return NULL; | |
135 | ||
136 | *s = (JsonSource) { | |
137 | .n_ref = 1, | |
138 | }; | |
139 | strcpy(s->name, name); | |
140 | ||
141 | return s; | |
142 | } | |
143 | ||
144 | DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(JsonSource, json_source, mfree); | |
145 | ||
146 | static bool json_source_equal(JsonSource *a, JsonSource *b) { | |
147 | if (a == b) | |
148 | return true; | |
149 | ||
150 | if (!a || !b) | |
151 | return false; | |
152 | ||
153 | return streq(a->name, b->name); | |
154 | } | |
155 | ||
156 | DEFINE_TRIVIAL_CLEANUP_FUNC(JsonSource*, json_source_unref); | |
157 | ||
d520d519 LP |
158 | /* There are four kind of JsonVariant* pointers: |
159 | * | |
160 | * 1. NULL | |
161 | * 2. A 'regular' one, i.e. pointing to malloc() memory | |
162 | * 3. A 'magic' one, i.e. one of the special JSON_VARIANT_MAGIC_XYZ values, that encode a few very basic values directly in the pointer. | |
163 | * 4. A 'const string' one, i.e. a pointer to a const string. | |
164 | * | |
165 | * The four kinds of pointers can be discerned like this: | |
166 | * | |
167 | * Detecting #1 is easy, just compare with NULL. Detecting #3 is similarly easy: all magic pointers are below | |
168 | * _JSON_VARIANT_MAGIC_MAX (which is pretty low, within the first memory page, which is special on Linux and other | |
169 | * OSes, as it is a faulting page). In order to discern #2 and #4 we check the lowest bit. If it's off it's #2, | |
170 | * otherwise #4. This makes use of the fact that malloc() will return "maximum aligned" memory, which definitely | |
171 | * means the pointer is even. This means we can use the uneven pointers to reference static strings, as long as we | |
172 | * make sure that all static strings used like this are aligned to 2 (or higher), and that we mask the bit on | |
173 | * access. The JSON_VARIANT_STRING_CONST() macro encodes strings as JsonVariant* pointers, with the bit set. */ | |
174 | ||
cd0b6c53 | 175 | static bool json_variant_is_magic(const JsonVariant *v) { |
4fcb507a LP |
176 | if (!v) |
177 | return false; | |
178 | ||
179 | return v < _JSON_VARIANT_MAGIC_MAX; | |
cd0b6c53 LP |
180 | } |
181 | ||
d520d519 LP |
182 | static bool json_variant_is_const_string(const JsonVariant *v) { |
183 | ||
184 | if (v < _JSON_VARIANT_MAGIC_MAX) | |
185 | return false; | |
186 | ||
187 | /* A proper JsonVariant is aligned to whatever malloc() aligns things too, which is definitely not uneven. We | |
188 | * hence use all uneven pointers as indicators for const strings. */ | |
189 | ||
190 | return (((uintptr_t) v) & 1) != 0; | |
191 | } | |
192 | ||
193 | static bool json_variant_is_regular(const JsonVariant *v) { | |
194 | ||
195 | if (v < _JSON_VARIANT_MAGIC_MAX) | |
196 | return false; | |
197 | ||
198 | return (((uintptr_t) v) & 1) == 0; | |
cd0b6c53 LP |
199 | } |
200 | ||
201 | static JsonVariant *json_variant_dereference(JsonVariant *v) { | |
202 | ||
203 | /* Recursively dereference variants that are references to other variants */ | |
204 | ||
205 | if (!v) | |
206 | return NULL; | |
207 | ||
d520d519 | 208 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
209 | return v; |
210 | ||
211 | if (!v->is_reference) | |
212 | return v; | |
213 | ||
214 | return json_variant_dereference(v->reference); | |
215 | } | |
216 | ||
b2fa0d4f LP |
217 | static uint16_t json_variant_depth(JsonVariant *v) { |
218 | ||
219 | v = json_variant_dereference(v); | |
220 | if (!v) | |
221 | return 0; | |
222 | ||
d520d519 | 223 | if (!json_variant_is_regular(v)) |
b2fa0d4f LP |
224 | return 0; |
225 | ||
226 | return v->depth; | |
227 | } | |
228 | ||
b7fc90a2 | 229 | static JsonVariant *json_variant_formalize(JsonVariant *v) { |
cd0b6c53 | 230 | |
b7fc90a2 LP |
231 | /* Converts json variant pointers to their normalized form, i.e. fully dereferenced and wherever |
232 | * possible converted to the "magic" version if there is one */ | |
cd0b6c53 LP |
233 | |
234 | if (!v) | |
235 | return NULL; | |
236 | ||
237 | v = json_variant_dereference(v); | |
238 | ||
239 | switch (json_variant_type(v)) { | |
240 | ||
241 | case JSON_VARIANT_BOOLEAN: | |
242 | return json_variant_boolean(v) ? JSON_VARIANT_MAGIC_TRUE : JSON_VARIANT_MAGIC_FALSE; | |
243 | ||
244 | case JSON_VARIANT_NULL: | |
245 | return JSON_VARIANT_MAGIC_NULL; | |
246 | ||
247 | case JSON_VARIANT_INTEGER: | |
248 | return json_variant_integer(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_INTEGER : v; | |
249 | ||
250 | case JSON_VARIANT_UNSIGNED: | |
251 | return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v; | |
252 | ||
253 | case JSON_VARIANT_REAL: | |
1561db8a | 254 | return iszero_safe(json_variant_real(v)) ? JSON_VARIANT_MAGIC_ZERO_REAL : v; |
cd0b6c53 LP |
255 | |
256 | case JSON_VARIANT_STRING: | |
257 | return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v; | |
258 | ||
259 | case JSON_VARIANT_ARRAY: | |
260 | return json_variant_elements(v) == 0 ? JSON_VARIANT_MAGIC_EMPTY_ARRAY : v; | |
261 | ||
262 | case JSON_VARIANT_OBJECT: | |
263 | return json_variant_elements(v) == 0 ? JSON_VARIANT_MAGIC_EMPTY_OBJECT : v; | |
264 | ||
265 | default: | |
266 | return v; | |
267 | } | |
268 | } | |
269 | ||
b7fc90a2 | 270 | static JsonVariant *json_variant_conservative_formalize(JsonVariant *v) { |
cd0b6c53 | 271 | |
bac06497 ZJS |
272 | /* Much like json_variant_formalize(), but won't simplify if the variant has a source/line location |
273 | * attached to it, in order not to lose context */ | |
cd0b6c53 LP |
274 | |
275 | if (!v) | |
276 | return NULL; | |
277 | ||
d520d519 | 278 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
279 | return v; |
280 | ||
281 | if (v->source || v->line > 0 || v->column > 0) | |
282 | return v; | |
283 | ||
b7fc90a2 | 284 | return json_variant_formalize(v); |
cd0b6c53 LP |
285 | } |
286 | ||
287 | static int json_variant_new(JsonVariant **ret, JsonVariantType type, size_t space) { | |
288 | JsonVariant *v; | |
289 | ||
290 | assert_return(ret, -EINVAL); | |
291 | ||
2eb1c198 LP |
292 | v = malloc0(MAX(sizeof(JsonVariant), |
293 | offsetof(JsonVariant, value) + space)); | |
cd0b6c53 LP |
294 | if (!v) |
295 | return -ENOMEM; | |
296 | ||
297 | v->n_ref = 1; | |
298 | v->type = type; | |
299 | ||
300 | *ret = v; | |
301 | return 0; | |
302 | } | |
303 | ||
718ca772 | 304 | int json_variant_new_integer(JsonVariant **ret, int64_t i) { |
cd0b6c53 LP |
305 | JsonVariant *v; |
306 | int r; | |
307 | ||
308 | assert_return(ret, -EINVAL); | |
309 | ||
310 | if (i == 0) { | |
311 | *ret = JSON_VARIANT_MAGIC_ZERO_INTEGER; | |
312 | return 0; | |
313 | } | |
314 | ||
315 | r = json_variant_new(&v, JSON_VARIANT_INTEGER, sizeof(i)); | |
316 | if (r < 0) | |
317 | return r; | |
318 | ||
319 | v->value.integer = i; | |
320 | *ret = v; | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
718ca772 | 325 | int json_variant_new_unsigned(JsonVariant **ret, uint64_t u) { |
cd0b6c53 LP |
326 | JsonVariant *v; |
327 | int r; | |
328 | ||
329 | assert_return(ret, -EINVAL); | |
330 | if (u == 0) { | |
331 | *ret = JSON_VARIANT_MAGIC_ZERO_UNSIGNED; | |
332 | return 0; | |
333 | } | |
334 | ||
335 | r = json_variant_new(&v, JSON_VARIANT_UNSIGNED, sizeof(u)); | |
336 | if (r < 0) | |
337 | return r; | |
338 | ||
339 | v->value.unsig = u; | |
340 | *ret = v; | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
337712e7 | 345 | int json_variant_new_real(JsonVariant **ret, double d) { |
cd0b6c53 LP |
346 | JsonVariant *v; |
347 | int r; | |
348 | ||
349 | assert_return(ret, -EINVAL); | |
350 | ||
1561db8a YW |
351 | r = fpclassify(d); |
352 | switch (r) { | |
353 | case FP_NAN: | |
354 | case FP_INFINITE: | |
355 | /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */ | |
356 | *ret = JSON_VARIANT_MAGIC_NULL; | |
cd0b6c53 | 357 | return 0; |
cd0b6c53 | 358 | |
1561db8a YW |
359 | case FP_ZERO: |
360 | *ret = JSON_VARIANT_MAGIC_ZERO_REAL; | |
8f1daefc LP |
361 | return 0; |
362 | } | |
363 | ||
cd0b6c53 LP |
364 | r = json_variant_new(&v, JSON_VARIANT_REAL, sizeof(d)); |
365 | if (r < 0) | |
366 | return r; | |
367 | ||
368 | v->value.real = d; | |
369 | *ret = v; | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
374 | int json_variant_new_boolean(JsonVariant **ret, bool b) { | |
375 | assert_return(ret, -EINVAL); | |
376 | ||
377 | if (b) | |
378 | *ret = JSON_VARIANT_MAGIC_TRUE; | |
379 | else | |
380 | *ret = JSON_VARIANT_MAGIC_FALSE; | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | int json_variant_new_null(JsonVariant **ret) { | |
386 | assert_return(ret, -EINVAL); | |
387 | ||
388 | *ret = JSON_VARIANT_MAGIC_NULL; | |
389 | return 0; | |
390 | } | |
391 | ||
392 | int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n) { | |
393 | JsonVariant *v; | |
394 | int r; | |
395 | ||
396 | assert_return(ret, -EINVAL); | |
397 | if (!s) { | |
f5fbe71d | 398 | assert_return(IN_SET(n, 0, SIZE_MAX), -EINVAL); |
cd0b6c53 LP |
399 | return json_variant_new_null(ret); |
400 | } | |
f5fbe71d | 401 | if (n == SIZE_MAX) /* determine length automatically */ |
cbb3092c LP |
402 | n = strlen(s); |
403 | else if (memchr(s, 0, n)) /* don't allow embedded NUL, as we can't express that in JSON */ | |
404 | return -EINVAL; | |
cd0b6c53 LP |
405 | if (n == 0) { |
406 | *ret = JSON_VARIANT_MAGIC_EMPTY_STRING; | |
407 | return 0; | |
408 | } | |
409 | ||
ea9afe00 ZJS |
410 | if (!utf8_is_valid_n(s, n)) /* JSON strings must be valid UTF-8 */ |
411 | return -EUCLEAN; | |
412 | ||
cd0b6c53 LP |
413 | r = json_variant_new(&v, JSON_VARIANT_STRING, n + 1); |
414 | if (r < 0) | |
415 | return r; | |
416 | ||
417 | memcpy(v->string, s, n); | |
418 | v->string[n] = 0; | |
419 | ||
420 | *ret = v; | |
421 | return 0; | |
422 | } | |
423 | ||
cc164891 LP |
424 | int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n) { |
425 | _cleanup_free_ char *s = NULL; | |
426 | ssize_t k; | |
427 | ||
428 | assert_return(ret, -EINVAL); | |
429 | assert_return(n == 0 || p, -EINVAL); | |
430 | ||
431 | k = base64mem(p, n, &s); | |
432 | if (k < 0) | |
433 | return k; | |
434 | ||
435 | return json_variant_new_stringn(ret, s, k); | |
436 | } | |
437 | ||
84738d86 LP |
438 | int json_variant_new_base32hex(JsonVariant **ret, const void *p, size_t n) { |
439 | _cleanup_free_ char *s = NULL; | |
440 | ||
441 | assert_return(ret, -EINVAL); | |
442 | assert_return(n == 0 || p, -EINVAL); | |
443 | ||
444 | s = base32hexmem(p, n, false); | |
445 | if (!s) | |
446 | return -ENOMEM; | |
447 | ||
448 | return json_variant_new_string(ret, s); | |
449 | } | |
450 | ||
2d64d2b9 LP |
451 | int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) { |
452 | _cleanup_free_ char *s = NULL; | |
453 | ||
454 | assert_return(ret, -EINVAL); | |
455 | assert_return(n == 0 || p, -EINVAL); | |
456 | ||
457 | s = hexmem(p, n); | |
458 | if (!s) | |
459 | return -ENOMEM; | |
460 | ||
461 | return json_variant_new_stringn(ret, s, n*2); | |
462 | } | |
463 | ||
84738d86 LP |
464 | int json_variant_new_octescape(JsonVariant **ret, const void *p, size_t n) { |
465 | _cleanup_free_ char *s = NULL; | |
466 | ||
467 | assert_return(ret, -EINVAL); | |
468 | assert_return(n == 0 || p, -EINVAL); | |
469 | ||
470 | s = octescape(p, n); | |
471 | if (!s) | |
472 | return -ENOMEM; | |
473 | ||
474 | return json_variant_new_string(ret, s); | |
475 | } | |
476 | ||
0b9481cf | 477 | int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) { |
85b55869 | 478 | return json_variant_new_string(ret, SD_ID128_TO_STRING(id)); |
0b9481cf LP |
479 | } |
480 | ||
9bc4156c LP |
481 | int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id) { |
482 | return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(id)); | |
483 | } | |
484 | ||
cd0b6c53 LP |
485 | static void json_variant_set(JsonVariant *a, JsonVariant *b) { |
486 | assert(a); | |
487 | ||
488 | b = json_variant_dereference(b); | |
489 | if (!b) { | |
490 | a->type = JSON_VARIANT_NULL; | |
491 | return; | |
492 | } | |
493 | ||
494 | a->type = json_variant_type(b); | |
495 | switch (a->type) { | |
496 | ||
497 | case JSON_VARIANT_INTEGER: | |
498 | a->value.integer = json_variant_integer(b); | |
499 | break; | |
500 | ||
501 | case JSON_VARIANT_UNSIGNED: | |
502 | a->value.unsig = json_variant_unsigned(b); | |
503 | break; | |
504 | ||
505 | case JSON_VARIANT_REAL: | |
506 | a->value.real = json_variant_real(b); | |
507 | break; | |
508 | ||
509 | case JSON_VARIANT_BOOLEAN: | |
510 | a->value.boolean = json_variant_boolean(b); | |
511 | break; | |
512 | ||
513 | case JSON_VARIANT_STRING: { | |
514 | const char *s; | |
515 | ||
516 | assert_se(s = json_variant_string(b)); | |
517 | ||
518 | /* Short strings we can store inline */ | |
519 | if (strnlen(s, INLINE_STRING_MAX+1) <= INLINE_STRING_MAX) { | |
520 | strcpy(a->string, s); | |
521 | break; | |
522 | } | |
523 | ||
524 | /* For longer strings, use a reference… */ | |
525 | _fallthrough_; | |
526 | } | |
527 | ||
528 | case JSON_VARIANT_ARRAY: | |
529 | case JSON_VARIANT_OBJECT: | |
530 | a->is_reference = true; | |
b7fc90a2 | 531 | a->reference = json_variant_ref(json_variant_conservative_formalize(b)); |
cd0b6c53 LP |
532 | break; |
533 | ||
534 | case JSON_VARIANT_NULL: | |
535 | break; | |
536 | ||
537 | default: | |
04499a70 | 538 | assert_not_reached(); |
cd0b6c53 LP |
539 | } |
540 | } | |
541 | ||
542 | static void json_variant_copy_source(JsonVariant *v, JsonVariant *from) { | |
543 | assert(v); | |
cd0b6c53 | 544 | |
d520d519 | 545 | if (!json_variant_is_regular(from)) |
cd0b6c53 LP |
546 | return; |
547 | ||
548 | v->line = from->line; | |
549 | v->column = from->column; | |
550 | v->source = json_source_ref(from->source); | |
551 | } | |
552 | ||
8525bb36 ZJS |
553 | static int _json_variant_array_put_element(JsonVariant *array, JsonVariant *element) { |
554 | assert(array); | |
555 | JsonVariant *w = array + 1 + array->n_elements; | |
556 | ||
557 | uint16_t d = json_variant_depth(element); | |
558 | if (d >= DEPTH_MAX) /* Refuse too deep nesting */ | |
559 | return -ELNRNG; | |
560 | if (d >= array->depth) | |
561 | array->depth = d + 1; | |
b3a9d980 | 562 | array->n_elements++; |
8525bb36 ZJS |
563 | |
564 | *w = (JsonVariant) { | |
565 | .is_embedded = true, | |
566 | .parent = array, | |
567 | }; | |
568 | ||
569 | json_variant_set(w, element); | |
570 | json_variant_copy_source(w, element); | |
571 | ||
572 | if (!json_variant_is_normalized(element)) | |
573 | array->normalized = false; | |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
cd0b6c53 | 578 | int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) { |
b2fa0d4f | 579 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
8525bb36 | 580 | int r; |
cd0b6c53 LP |
581 | |
582 | assert_return(ret, -EINVAL); | |
583 | if (n == 0) { | |
584 | *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY; | |
585 | return 0; | |
586 | } | |
587 | assert_return(array, -EINVAL); | |
588 | ||
589 | v = new(JsonVariant, n + 1); | |
590 | if (!v) | |
591 | return -ENOMEM; | |
592 | ||
593 | *v = (JsonVariant) { | |
594 | .n_ref = 1, | |
595 | .type = JSON_VARIANT_ARRAY, | |
8525bb36 | 596 | .normalized = true, |
cd0b6c53 LP |
597 | }; |
598 | ||
8525bb36 ZJS |
599 | while (v->n_elements < n) { |
600 | r = _json_variant_array_put_element(v, array[v->n_elements]); | |
601 | if (r < 0) | |
602 | return r; | |
cd0b6c53 LP |
603 | } |
604 | ||
b2fa0d4f | 605 | *ret = TAKE_PTR(v); |
cd0b6c53 LP |
606 | return 0; |
607 | } | |
608 | ||
609 | int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) { | |
cd0b6c53 LP |
610 | assert_return(ret, -EINVAL); |
611 | if (n == 0) { | |
612 | *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY; | |
613 | return 0; | |
614 | } | |
615 | assert_return(p, -EINVAL); | |
616 | ||
a4669764 | 617 | JsonVariant *v = new(JsonVariant, n + 1); |
cd0b6c53 LP |
618 | if (!v) |
619 | return -ENOMEM; | |
620 | ||
621 | *v = (JsonVariant) { | |
622 | .n_ref = 1, | |
623 | .type = JSON_VARIANT_ARRAY, | |
624 | .n_elements = n, | |
b2fa0d4f | 625 | .depth = 1, |
cd0b6c53 LP |
626 | }; |
627 | ||
a4669764 | 628 | for (size_t i = 0; i < n; i++) { |
cd0b6c53 LP |
629 | JsonVariant *w = v + 1 + i; |
630 | ||
631 | *w = (JsonVariant) { | |
632 | .is_embedded = true, | |
633 | .parent = v, | |
a1559e3f LP |
634 | .type = JSON_VARIANT_UNSIGNED, |
635 | .value.unsig = ((const uint8_t*) p)[i], | |
cd0b6c53 | 636 | }; |
cd0b6c53 LP |
637 | } |
638 | ||
b7fc90a2 LP |
639 | v->normalized = true; |
640 | ||
cd0b6c53 LP |
641 | *ret = v; |
642 | return 0; | |
643 | } | |
644 | ||
645 | int json_variant_new_array_strv(JsonVariant **ret, char **l) { | |
646 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
647 | size_t n; | |
648 | int r; | |
649 | ||
650 | assert(ret); | |
651 | ||
652 | n = strv_length(l); | |
653 | if (n == 0) { | |
654 | *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY; | |
655 | return 0; | |
656 | } | |
657 | ||
a1559e3f | 658 | v = new(JsonVariant, n + 1); |
cd0b6c53 LP |
659 | if (!v) |
660 | return -ENOMEM; | |
661 | ||
662 | *v = (JsonVariant) { | |
663 | .n_ref = 1, | |
664 | .type = JSON_VARIANT_ARRAY, | |
b2fa0d4f | 665 | .depth = 1, |
cd0b6c53 LP |
666 | }; |
667 | ||
668 | for (v->n_elements = 0; v->n_elements < n; v->n_elements++) { | |
669 | JsonVariant *w = v + 1 + v->n_elements; | |
670 | size_t k; | |
671 | ||
a1559e3f LP |
672 | *w = (JsonVariant) { |
673 | .is_embedded = true, | |
674 | .parent = v, | |
675 | .type = JSON_VARIANT_STRING, | |
676 | }; | |
cd0b6c53 LP |
677 | |
678 | k = strlen(l[v->n_elements]); | |
679 | ||
680 | if (k > INLINE_STRING_MAX) { | |
681 | /* If string is too long, store it as reference. */ | |
682 | ||
cbb3092c | 683 | r = json_variant_new_string(&w->reference, l[v->n_elements]); |
cd0b6c53 LP |
684 | if (r < 0) |
685 | return r; | |
686 | ||
687 | w->is_reference = true; | |
ea9afe00 ZJS |
688 | } else { |
689 | if (!utf8_is_valid_n(l[v->n_elements], k)) /* JSON strings must be valid UTF-8 */ | |
690 | return -EUCLEAN; | |
691 | ||
cd0b6c53 | 692 | memcpy(w->string, l[v->n_elements], k+1); |
ea9afe00 | 693 | } |
cd0b6c53 LP |
694 | } |
695 | ||
b7fc90a2 LP |
696 | v->normalized = true; |
697 | ||
cd0b6c53 LP |
698 | *ret = TAKE_PTR(v); |
699 | return 0; | |
700 | } | |
701 | ||
702 | int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) { | |
b2fa0d4f | 703 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
b7fc90a2 LP |
704 | const char *prev = NULL; |
705 | bool sorted = true, normalized = true; | |
cd0b6c53 LP |
706 | |
707 | assert_return(ret, -EINVAL); | |
708 | if (n == 0) { | |
709 | *ret = JSON_VARIANT_MAGIC_EMPTY_OBJECT; | |
710 | return 0; | |
711 | } | |
712 | assert_return(array, -EINVAL); | |
713 | assert_return(n % 2 == 0, -EINVAL); | |
714 | ||
715 | v = new(JsonVariant, n + 1); | |
716 | if (!v) | |
717 | return -ENOMEM; | |
718 | ||
719 | *v = (JsonVariant) { | |
720 | .n_ref = 1, | |
721 | .type = JSON_VARIANT_OBJECT, | |
cd0b6c53 LP |
722 | }; |
723 | ||
b2fa0d4f LP |
724 | for (v->n_elements = 0; v->n_elements < n; v->n_elements++) { |
725 | JsonVariant *w = v + 1 + v->n_elements, | |
bac06497 | 726 | *c = array[v->n_elements]; |
b2fa0d4f LP |
727 | uint16_t d; |
728 | ||
b7fc90a2 LP |
729 | if ((v->n_elements & 1) == 0) { |
730 | const char *k; | |
731 | ||
732 | if (!json_variant_is_string(c)) | |
733 | return -EINVAL; /* Every second one needs to be a string, as it is the key name */ | |
734 | ||
735 | assert_se(k = json_variant_string(c)); | |
736 | ||
737 | if (prev && strcmp(k, prev) <= 0) | |
738 | sorted = normalized = false; | |
739 | ||
740 | prev = k; | |
741 | } else if (!json_variant_is_normalized(c)) | |
742 | normalized = false; | |
d77e781f | 743 | |
b2fa0d4f LP |
744 | d = json_variant_depth(c); |
745 | if (d >= DEPTH_MAX) /* Refuse too deep nesting */ | |
746 | return -ELNRNG; | |
747 | if (d >= v->depth) | |
748 | v->depth = d + 1; | |
cd0b6c53 LP |
749 | |
750 | *w = (JsonVariant) { | |
751 | .is_embedded = true, | |
752 | .parent = v, | |
753 | }; | |
754 | ||
b2fa0d4f LP |
755 | json_variant_set(w, c); |
756 | json_variant_copy_source(w, c); | |
cd0b6c53 LP |
757 | } |
758 | ||
b7fc90a2 LP |
759 | v->normalized = normalized; |
760 | v->sorted = sorted; | |
761 | ||
b2fa0d4f | 762 | *ret = TAKE_PTR(v); |
cd0b6c53 LP |
763 | return 0; |
764 | } | |
765 | ||
83bc6cb7 | 766 | static size_t json_variant_size(JsonVariant* v) { |
83bc6cb7 LP |
767 | if (!json_variant_is_regular(v)) |
768 | return 0; | |
769 | ||
770 | if (v->is_reference) | |
771 | return offsetof(JsonVariant, reference) + sizeof(JsonVariant*); | |
772 | ||
773 | switch (v->type) { | |
774 | ||
775 | case JSON_VARIANT_STRING: | |
776 | return offsetof(JsonVariant, string) + strlen(v->string) + 1; | |
777 | ||
778 | case JSON_VARIANT_REAL: | |
337712e7 | 779 | return offsetof(JsonVariant, value) + sizeof(double); |
83bc6cb7 LP |
780 | |
781 | case JSON_VARIANT_UNSIGNED: | |
718ca772 | 782 | return offsetof(JsonVariant, value) + sizeof(uint64_t); |
83bc6cb7 LP |
783 | |
784 | case JSON_VARIANT_INTEGER: | |
718ca772 | 785 | return offsetof(JsonVariant, value) + sizeof(int64_t); |
83bc6cb7 LP |
786 | |
787 | case JSON_VARIANT_BOOLEAN: | |
788 | return offsetof(JsonVariant, value) + sizeof(bool); | |
789 | ||
790 | case JSON_VARIANT_ARRAY: | |
791 | case JSON_VARIANT_OBJECT: | |
792 | return offsetof(JsonVariant, n_elements) + sizeof(size_t); | |
793 | ||
794 | case JSON_VARIANT_NULL: | |
795 | return offsetof(JsonVariant, value); | |
796 | ||
797 | default: | |
04499a70 | 798 | assert_not_reached(); |
83bc6cb7 LP |
799 | } |
800 | } | |
801 | ||
802 | static void json_variant_free_inner(JsonVariant *v, bool force_sensitive) { | |
803 | bool sensitive; | |
804 | ||
cd0b6c53 LP |
805 | assert(v); |
806 | ||
d520d519 | 807 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
808 | return; |
809 | ||
810 | json_source_unref(v->source); | |
811 | ||
83bc6cb7 LP |
812 | sensitive = v->sensitive || force_sensitive; |
813 | ||
cd0b6c53 | 814 | if (v->is_reference) { |
83bc6cb7 LP |
815 | if (sensitive) |
816 | json_variant_sensitive(v->reference); | |
817 | ||
cd0b6c53 LP |
818 | json_variant_unref(v->reference); |
819 | return; | |
820 | } | |
821 | ||
a4669764 ZJS |
822 | if (IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) |
823 | for (size_t i = 0; i < v->n_elements; i++) | |
83bc6cb7 | 824 | json_variant_free_inner(v + 1 + i, sensitive); |
83bc6cb7 LP |
825 | |
826 | if (sensitive) | |
827 | explicit_bzero_safe(v, json_variant_size(v)); | |
cd0b6c53 LP |
828 | } |
829 | ||
8525bb36 ZJS |
830 | static unsigned json_variant_n_ref(const JsonVariant *v) { |
831 | /* Return the number of references to v. | |
832 | * 0 => NULL or not a regular object or embedded. | |
833 | * >0 => number of references | |
834 | */ | |
835 | ||
836 | if (!v || !json_variant_is_regular(v) || v->is_embedded) | |
837 | return 0; | |
838 | ||
839 | assert(v->n_ref > 0); | |
840 | return v->n_ref; | |
841 | } | |
842 | ||
cd0b6c53 LP |
843 | JsonVariant *json_variant_ref(JsonVariant *v) { |
844 | if (!v) | |
845 | return NULL; | |
d520d519 | 846 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
847 | return v; |
848 | ||
849 | if (v->is_embedded) | |
850 | json_variant_ref(v->parent); /* ref the compounding variant instead */ | |
851 | else { | |
852 | assert(v->n_ref > 0); | |
853 | v->n_ref++; | |
854 | } | |
855 | ||
856 | return v; | |
857 | } | |
858 | ||
859 | JsonVariant *json_variant_unref(JsonVariant *v) { | |
860 | if (!v) | |
861 | return NULL; | |
d520d519 | 862 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
863 | return NULL; |
864 | ||
865 | if (v->is_embedded) | |
866 | json_variant_unref(v->parent); | |
867 | else { | |
868 | assert(v->n_ref > 0); | |
869 | v->n_ref--; | |
870 | ||
871 | if (v->n_ref == 0) { | |
83bc6cb7 | 872 | json_variant_free_inner(v, false); |
cd0b6c53 LP |
873 | free(v); |
874 | } | |
875 | } | |
876 | ||
877 | return NULL; | |
878 | } | |
879 | ||
880 | void json_variant_unref_many(JsonVariant **array, size_t n) { | |
cd0b6c53 LP |
881 | assert(array || n == 0); |
882 | ||
a4669764 | 883 | for (size_t i = 0; i < n; i++) |
cd0b6c53 | 884 | json_variant_unref(array[i]); |
ee9d31a6 DDM |
885 | |
886 | free(array); | |
cd0b6c53 LP |
887 | } |
888 | ||
889 | const char *json_variant_string(JsonVariant *v) { | |
890 | if (!v) | |
891 | return NULL; | |
892 | if (v == JSON_VARIANT_MAGIC_EMPTY_STRING) | |
893 | return ""; | |
894 | if (json_variant_is_magic(v)) | |
895 | goto mismatch; | |
d520d519 LP |
896 | if (json_variant_is_const_string(v)) { |
897 | uintptr_t p = (uintptr_t) v; | |
898 | ||
899 | assert((p & 1) != 0); | |
900 | return (const char*) (p ^ 1U); | |
901 | } | |
902 | ||
cd0b6c53 LP |
903 | if (v->is_reference) |
904 | return json_variant_string(v->reference); | |
905 | if (v->type != JSON_VARIANT_STRING) | |
906 | goto mismatch; | |
907 | ||
908 | return v->string; | |
909 | ||
910 | mismatch: | |
911 | log_debug("Non-string JSON variant requested as string, returning NULL."); | |
912 | return NULL; | |
913 | } | |
914 | ||
915 | bool json_variant_boolean(JsonVariant *v) { | |
916 | if (!v) | |
917 | goto mismatch; | |
918 | if (v == JSON_VARIANT_MAGIC_TRUE) | |
919 | return true; | |
920 | if (v == JSON_VARIANT_MAGIC_FALSE) | |
921 | return false; | |
d520d519 | 922 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
923 | goto mismatch; |
924 | if (v->type != JSON_VARIANT_BOOLEAN) | |
925 | goto mismatch; | |
926 | if (v->is_reference) | |
927 | return json_variant_boolean(v->reference); | |
928 | ||
929 | return v->value.boolean; | |
930 | ||
931 | mismatch: | |
932 | log_debug("Non-boolean JSON variant requested as boolean, returning false."); | |
933 | return false; | |
934 | } | |
935 | ||
718ca772 | 936 | int64_t json_variant_integer(JsonVariant *v) { |
cd0b6c53 LP |
937 | if (!v) |
938 | goto mismatch; | |
939 | if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER || | |
940 | v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || | |
941 | v == JSON_VARIANT_MAGIC_ZERO_REAL) | |
942 | return 0; | |
d520d519 | 943 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
944 | goto mismatch; |
945 | if (v->is_reference) | |
946 | return json_variant_integer(v->reference); | |
947 | ||
948 | switch (v->type) { | |
949 | ||
950 | case JSON_VARIANT_INTEGER: | |
951 | return v->value.integer; | |
952 | ||
953 | case JSON_VARIANT_UNSIGNED: | |
718ca772 ZJS |
954 | if (v->value.unsig <= INT64_MAX) |
955 | return (int64_t) v->value.unsig; | |
cd0b6c53 | 956 | |
c0f86d66 | 957 | log_debug("Unsigned integer %" PRIu64 " requested as signed integer and out of range, returning 0.", v->value.unsig); |
cd0b6c53 LP |
958 | return 0; |
959 | ||
960 | case JSON_VARIANT_REAL: { | |
718ca772 | 961 | int64_t converted; |
cd0b6c53 | 962 | |
718ca772 | 963 | converted = (int64_t) v->value.real; |
cd0b6c53 | 964 | |
1561db8a | 965 | if (fp_equal((double) converted, v->value.real)) |
cd0b6c53 LP |
966 | return converted; |
967 | ||
337712e7 | 968 | log_debug("Real %g requested as integer, and cannot be converted losslessly, returning 0.", v->value.real); |
cd0b6c53 LP |
969 | return 0; |
970 | } | |
971 | ||
972 | default: | |
973 | break; | |
974 | } | |
975 | ||
976 | mismatch: | |
977 | log_debug("Non-integer JSON variant requested as integer, returning 0."); | |
978 | return 0; | |
979 | } | |
980 | ||
718ca772 | 981 | uint64_t json_variant_unsigned(JsonVariant *v) { |
cd0b6c53 LP |
982 | if (!v) |
983 | goto mismatch; | |
984 | if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER || | |
985 | v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || | |
986 | v == JSON_VARIANT_MAGIC_ZERO_REAL) | |
987 | return 0; | |
d520d519 | 988 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
989 | goto mismatch; |
990 | if (v->is_reference) | |
991 | return json_variant_integer(v->reference); | |
992 | ||
993 | switch (v->type) { | |
994 | ||
995 | case JSON_VARIANT_INTEGER: | |
996 | if (v->value.integer >= 0) | |
718ca772 | 997 | return (uint64_t) v->value.integer; |
cd0b6c53 | 998 | |
c0f86d66 | 999 | log_debug("Signed integer %" PRIi64 " requested as unsigned integer and out of range, returning 0.", v->value.integer); |
cd0b6c53 LP |
1000 | return 0; |
1001 | ||
1002 | case JSON_VARIANT_UNSIGNED: | |
1003 | return v->value.unsig; | |
1004 | ||
1005 | case JSON_VARIANT_REAL: { | |
718ca772 | 1006 | uint64_t converted; |
cd0b6c53 | 1007 | |
718ca772 | 1008 | converted = (uint64_t) v->value.real; |
cd0b6c53 | 1009 | |
1561db8a | 1010 | if (fp_equal((double) converted, v->value.real)) |
cd0b6c53 LP |
1011 | return converted; |
1012 | ||
337712e7 | 1013 | log_debug("Real %g requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real); |
cd0b6c53 LP |
1014 | return 0; |
1015 | } | |
1016 | ||
1017 | default: | |
1018 | break; | |
1019 | } | |
1020 | ||
1021 | mismatch: | |
1022 | log_debug("Non-integer JSON variant requested as unsigned, returning 0."); | |
1023 | return 0; | |
1024 | } | |
1025 | ||
337712e7 | 1026 | double json_variant_real(JsonVariant *v) { |
cd0b6c53 LP |
1027 | if (!v) |
1028 | return 0.0; | |
1029 | if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER || | |
1030 | v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || | |
1031 | v == JSON_VARIANT_MAGIC_ZERO_REAL) | |
1032 | return 0.0; | |
d520d519 | 1033 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
1034 | goto mismatch; |
1035 | if (v->is_reference) | |
1036 | return json_variant_real(v->reference); | |
1037 | ||
1038 | switch (v->type) { | |
1039 | ||
1040 | case JSON_VARIANT_REAL: | |
1041 | return v->value.real; | |
1042 | ||
1043 | case JSON_VARIANT_INTEGER: { | |
337712e7 | 1044 | double converted = (double) v->value.integer; |
cd0b6c53 | 1045 | |
718ca772 | 1046 | if ((int64_t) converted == v->value.integer) |
cd0b6c53 LP |
1047 | return converted; |
1048 | ||
c0f86d66 | 1049 | log_debug("Signed integer %" PRIi64 " requested as real, and cannot be converted losslessly, returning 0.", v->value.integer); |
cd0b6c53 LP |
1050 | return 0.0; |
1051 | } | |
1052 | ||
1053 | case JSON_VARIANT_UNSIGNED: { | |
337712e7 | 1054 | double converted = (double) v->value.unsig; |
cd0b6c53 | 1055 | |
718ca772 | 1056 | if ((uint64_t) converted == v->value.unsig) |
cd0b6c53 LP |
1057 | return converted; |
1058 | ||
c0f86d66 | 1059 | log_debug("Unsigned integer %" PRIu64 " requested as real, and cannot be converted losslessly, returning 0.", v->value.unsig); |
cd0b6c53 LP |
1060 | return 0.0; |
1061 | } | |
1062 | ||
1063 | default: | |
1064 | break; | |
1065 | } | |
1066 | ||
1067 | mismatch: | |
1068 | log_debug("Non-integer JSON variant requested as integer, returning 0."); | |
92853e9b | 1069 | return 0.0; |
cd0b6c53 LP |
1070 | } |
1071 | ||
1072 | bool json_variant_is_negative(JsonVariant *v) { | |
1073 | if (!v) | |
1074 | goto mismatch; | |
1075 | if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER || | |
1076 | v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || | |
1077 | v == JSON_VARIANT_MAGIC_ZERO_REAL) | |
1078 | return false; | |
d520d519 | 1079 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
1080 | goto mismatch; |
1081 | if (v->is_reference) | |
1082 | return json_variant_is_negative(v->reference); | |
1083 | ||
1084 | /* This function is useful as checking whether numbers are negative is pretty complex since we have three types | |
1085 | * of numbers. And some JSON code (OCI for example) uses negative numbers to mark "not defined" numeric | |
1086 | * values. */ | |
1087 | ||
1088 | switch (v->type) { | |
1089 | ||
1090 | case JSON_VARIANT_REAL: | |
1091 | return v->value.real < 0; | |
1092 | ||
1093 | case JSON_VARIANT_INTEGER: | |
1094 | return v->value.integer < 0; | |
1095 | ||
1096 | case JSON_VARIANT_UNSIGNED: | |
1097 | return false; | |
1098 | ||
1099 | default: | |
1100 | break; | |
1101 | } | |
1102 | ||
1103 | mismatch: | |
1104 | log_debug("Non-integer JSON variant tested for negativity, returning false."); | |
1105 | return false; | |
1106 | } | |
1107 | ||
e787b211 LP |
1108 | bool json_variant_is_blank_object(JsonVariant *v) { |
1109 | /* Returns true if the specified object is null or empty */ | |
1110 | return !v || | |
1111 | json_variant_is_null(v) || | |
1112 | (json_variant_is_object(v) && json_variant_elements(v) == 0); | |
1113 | } | |
1114 | ||
1115 | bool json_variant_is_blank_array(JsonVariant *v) { | |
1116 | return !v || | |
1117 | json_variant_is_null(v) || | |
1118 | (json_variant_is_array(v) && json_variant_elements(v) == 0); | |
1119 | } | |
1120 | ||
cd0b6c53 LP |
1121 | JsonVariantType json_variant_type(JsonVariant *v) { |
1122 | ||
1123 | if (!v) | |
1124 | return _JSON_VARIANT_TYPE_INVALID; | |
1125 | ||
d520d519 LP |
1126 | if (json_variant_is_const_string(v)) |
1127 | return JSON_VARIANT_STRING; | |
1128 | ||
cd0b6c53 LP |
1129 | if (v == JSON_VARIANT_MAGIC_TRUE || v == JSON_VARIANT_MAGIC_FALSE) |
1130 | return JSON_VARIANT_BOOLEAN; | |
1131 | ||
1132 | if (v == JSON_VARIANT_MAGIC_NULL) | |
1133 | return JSON_VARIANT_NULL; | |
1134 | ||
1135 | if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER) | |
1136 | return JSON_VARIANT_INTEGER; | |
1137 | ||
1138 | if (v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED) | |
1139 | return JSON_VARIANT_UNSIGNED; | |
1140 | ||
1141 | if (v == JSON_VARIANT_MAGIC_ZERO_REAL) | |
1142 | return JSON_VARIANT_REAL; | |
1143 | ||
1144 | if (v == JSON_VARIANT_MAGIC_EMPTY_STRING) | |
1145 | return JSON_VARIANT_STRING; | |
1146 | ||
1147 | if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY) | |
1148 | return JSON_VARIANT_ARRAY; | |
1149 | ||
1150 | if (v == JSON_VARIANT_MAGIC_EMPTY_OBJECT) | |
1151 | return JSON_VARIANT_OBJECT; | |
1152 | ||
1153 | return v->type; | |
1154 | } | |
1155 | ||
337712e7 ZJS |
1156 | _function_no_sanitize_float_cast_overflow_ |
1157 | bool json_variant_has_type(JsonVariant *v, JsonVariantType type) { | |
cd0b6c53 LP |
1158 | JsonVariantType rt; |
1159 | ||
337712e7 | 1160 | /* Note: we turn off ubsan float cast overflow detection for this function, since it would complain |
8e2fa6e2 LP |
1161 | * about our float casts but we do them explicitly to detect conversion errors. */ |
1162 | ||
cd0b6c53 | 1163 | v = json_variant_dereference(v); |
f8c186c9 LP |
1164 | if (!v) |
1165 | return false; | |
cd0b6c53 LP |
1166 | |
1167 | rt = json_variant_type(v); | |
1168 | if (rt == type) | |
1169 | return true; | |
1170 | ||
d520d519 LP |
1171 | /* If it's a const string, then it only can be a string, and if it is not, it's not */ |
1172 | if (json_variant_is_const_string(v)) | |
1173 | return false; | |
1174 | ||
cd0b6c53 LP |
1175 | /* All three magic zeroes qualify as integer, unsigned and as real */ |
1176 | if ((v == JSON_VARIANT_MAGIC_ZERO_INTEGER || v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || v == JSON_VARIANT_MAGIC_ZERO_REAL) && | |
1177 | IN_SET(type, JSON_VARIANT_INTEGER, JSON_VARIANT_UNSIGNED, JSON_VARIANT_REAL, JSON_VARIANT_NUMBER)) | |
1178 | return true; | |
1179 | ||
1180 | /* All other magic variant types are only equal to themselves */ | |
1181 | if (json_variant_is_magic(v)) | |
1182 | return false; | |
1183 | ||
1184 | /* Handle the "number" pseudo type */ | |
1185 | if (type == JSON_VARIANT_NUMBER) | |
1186 | return IN_SET(rt, JSON_VARIANT_INTEGER, JSON_VARIANT_UNSIGNED, JSON_VARIANT_REAL); | |
1187 | ||
1188 | /* Integer conversions are OK in many cases */ | |
1189 | if (rt == JSON_VARIANT_INTEGER && type == JSON_VARIANT_UNSIGNED) | |
1190 | return v->value.integer >= 0; | |
1191 | if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_INTEGER) | |
718ca772 | 1192 | return v->value.unsig <= INT64_MAX; |
cd0b6c53 LP |
1193 | |
1194 | /* Any integer that can be converted lossley to a real and back may also be considered a real */ | |
1195 | if (rt == JSON_VARIANT_INTEGER && type == JSON_VARIANT_REAL) | |
718ca772 | 1196 | return (int64_t) (double) v->value.integer == v->value.integer; |
cd0b6c53 | 1197 | if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL) |
718ca772 | 1198 | return (uint64_t) (double) v->value.unsig == v->value.unsig; |
cd0b6c53 | 1199 | |
cd0b6c53 LP |
1200 | /* Any real that can be converted losslessly to an integer and back may also be considered an integer */ |
1201 | if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER) | |
1561db8a | 1202 | return fp_equal((double) (int64_t) v->value.real, v->value.real); |
cd0b6c53 | 1203 | if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED) |
1561db8a | 1204 | return fp_equal((double) (uint64_t) v->value.real, v->value.real); |
cd0b6c53 LP |
1205 | |
1206 | return false; | |
1207 | } | |
1208 | ||
1209 | size_t json_variant_elements(JsonVariant *v) { | |
1210 | if (!v) | |
1211 | return 0; | |
1212 | if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY || | |
1213 | v == JSON_VARIANT_MAGIC_EMPTY_OBJECT) | |
1214 | return 0; | |
d520d519 | 1215 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
1216 | goto mismatch; |
1217 | if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) | |
1218 | goto mismatch; | |
1219 | if (v->is_reference) | |
1220 | return json_variant_elements(v->reference); | |
1221 | ||
1222 | return v->n_elements; | |
1223 | ||
1224 | mismatch: | |
1225 | log_debug("Number of elements in non-array/non-object JSON variant requested, returning 0."); | |
1226 | return 0; | |
1227 | } | |
1228 | ||
1229 | JsonVariant *json_variant_by_index(JsonVariant *v, size_t idx) { | |
1230 | if (!v) | |
1231 | return NULL; | |
1232 | if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY || | |
1233 | v == JSON_VARIANT_MAGIC_EMPTY_OBJECT) | |
1234 | return NULL; | |
d520d519 | 1235 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
1236 | goto mismatch; |
1237 | if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) | |
1238 | goto mismatch; | |
1239 | if (v->is_reference) | |
1240 | return json_variant_by_index(v->reference, idx); | |
1241 | if (idx >= v->n_elements) | |
1242 | return NULL; | |
1243 | ||
b7fc90a2 | 1244 | return json_variant_conservative_formalize(v + 1 + idx); |
cd0b6c53 LP |
1245 | |
1246 | mismatch: | |
1247 | log_debug("Element in non-array/non-object JSON variant requested by index, returning NULL."); | |
1248 | return NULL; | |
1249 | } | |
1250 | ||
1251 | JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVariant **ret_key) { | |
cd0b6c53 LP |
1252 | if (!v) |
1253 | goto not_found; | |
1254 | if (!key) | |
1255 | goto not_found; | |
1256 | if (v == JSON_VARIANT_MAGIC_EMPTY_OBJECT) | |
1257 | goto not_found; | |
d520d519 | 1258 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
1259 | goto mismatch; |
1260 | if (v->type != JSON_VARIANT_OBJECT) | |
1261 | goto mismatch; | |
1262 | if (v->is_reference) | |
1263 | return json_variant_by_key(v->reference, key); | |
1264 | ||
b7fc90a2 LP |
1265 | if (v->sorted) { |
1266 | size_t a = 0, b = v->n_elements/2; | |
1267 | ||
1268 | /* If the variant is sorted we can use bisection to find the entry we need in O(log(n)) time */ | |
1269 | ||
1270 | while (b > a) { | |
1271 | JsonVariant *p; | |
1272 | const char *f; | |
a4669764 | 1273 | size_t i; |
b7fc90a2 LP |
1274 | int c; |
1275 | ||
1276 | i = (a + b) / 2; | |
1277 | p = json_variant_dereference(v + 1 + i*2); | |
1278 | ||
1279 | assert_se(f = json_variant_string(p)); | |
1280 | ||
1281 | c = strcmp(key, f); | |
1282 | if (c == 0) { | |
1283 | if (ret_key) | |
1284 | *ret_key = json_variant_conservative_formalize(v + 1 + i*2); | |
1285 | ||
1286 | return json_variant_conservative_formalize(v + 1 + i*2 + 1); | |
1287 | } else if (c < 0) | |
1288 | b = i; | |
1289 | else | |
1290 | a = i + 1; | |
1291 | } | |
1292 | ||
1293 | goto not_found; | |
1294 | } | |
1295 | ||
1296 | /* The variant is not sorted, hence search for the field linearly */ | |
a4669764 | 1297 | for (size_t i = 0; i < v->n_elements; i += 2) { |
cd0b6c53 LP |
1298 | JsonVariant *p; |
1299 | ||
1300 | p = json_variant_dereference(v + 1 + i); | |
1301 | ||
1302 | if (!json_variant_has_type(p, JSON_VARIANT_STRING)) | |
1303 | continue; | |
1304 | ||
1305 | if (streq(json_variant_string(p), key)) { | |
1306 | ||
1307 | if (ret_key) | |
b7fc90a2 | 1308 | *ret_key = json_variant_conservative_formalize(v + 1 + i); |
cd0b6c53 | 1309 | |
b7fc90a2 | 1310 | return json_variant_conservative_formalize(v + 1 + i + 1); |
cd0b6c53 LP |
1311 | } |
1312 | } | |
1313 | ||
1314 | not_found: | |
1315 | if (ret_key) | |
1316 | *ret_key = NULL; | |
1317 | ||
1318 | return NULL; | |
1319 | ||
1320 | mismatch: | |
1321 | log_debug("Element in non-object JSON variant requested by key, returning NULL."); | |
1322 | if (ret_key) | |
1323 | *ret_key = NULL; | |
1324 | ||
1325 | return NULL; | |
1326 | } | |
1327 | ||
1328 | JsonVariant *json_variant_by_key(JsonVariant *v, const char *key) { | |
1329 | return json_variant_by_key_full(v, key, NULL); | |
1330 | } | |
1331 | ||
1332 | bool json_variant_equal(JsonVariant *a, JsonVariant *b) { | |
1333 | JsonVariantType t; | |
1334 | ||
b7fc90a2 LP |
1335 | a = json_variant_formalize(a); |
1336 | b = json_variant_formalize(b); | |
cd0b6c53 LP |
1337 | |
1338 | if (a == b) | |
1339 | return true; | |
1340 | ||
1341 | t = json_variant_type(a); | |
1342 | if (!json_variant_has_type(b, t)) | |
1343 | return false; | |
1344 | ||
1345 | switch (t) { | |
1346 | ||
1347 | case JSON_VARIANT_STRING: | |
1348 | return streq(json_variant_string(a), json_variant_string(b)); | |
1349 | ||
1350 | case JSON_VARIANT_INTEGER: | |
1351 | return json_variant_integer(a) == json_variant_integer(b); | |
1352 | ||
1353 | case JSON_VARIANT_UNSIGNED: | |
1354 | return json_variant_unsigned(a) == json_variant_unsigned(b); | |
1355 | ||
1356 | case JSON_VARIANT_REAL: | |
1561db8a | 1357 | return fp_equal(json_variant_real(a), json_variant_real(b)); |
cd0b6c53 LP |
1358 | |
1359 | case JSON_VARIANT_BOOLEAN: | |
1360 | return json_variant_boolean(a) == json_variant_boolean(b); | |
1361 | ||
1362 | case JSON_VARIANT_NULL: | |
1363 | return true; | |
1364 | ||
1365 | case JSON_VARIANT_ARRAY: { | |
a4669764 | 1366 | size_t n = json_variant_elements(a); |
cd0b6c53 LP |
1367 | if (n != json_variant_elements(b)) |
1368 | return false; | |
1369 | ||
a4669764 | 1370 | for (size_t i = 0; i < n; i++) |
cd0b6c53 LP |
1371 | if (!json_variant_equal(json_variant_by_index(a, i), json_variant_by_index(b, i))) |
1372 | return false; | |
cd0b6c53 LP |
1373 | |
1374 | return true; | |
1375 | } | |
1376 | ||
1377 | case JSON_VARIANT_OBJECT: { | |
a4669764 | 1378 | size_t n = json_variant_elements(a); |
cd0b6c53 LP |
1379 | if (n != json_variant_elements(b)) |
1380 | return false; | |
1381 | ||
1382 | /* Iterate through all keys in 'a' */ | |
a4669764 | 1383 | for (size_t i = 0; i < n; i += 2) { |
cd0b6c53 | 1384 | bool found = false; |
cd0b6c53 LP |
1385 | |
1386 | /* Match them against all keys in 'b' */ | |
a4669764 | 1387 | for (size_t j = 0; j < n; j += 2) { |
cd0b6c53 LP |
1388 | JsonVariant *key_b; |
1389 | ||
1390 | key_b = json_variant_by_index(b, j); | |
1391 | ||
1392 | /* During the first iteration unmark everything */ | |
1393 | if (i == 0) | |
1394 | key_b->is_marked = false; | |
1395 | else if (key_b->is_marked) /* In later iterations if we already marked something, don't bother with it again */ | |
1396 | continue; | |
1397 | ||
1398 | if (found) | |
1399 | continue; | |
1400 | ||
1401 | if (json_variant_equal(json_variant_by_index(a, i), key_b) && | |
1402 | json_variant_equal(json_variant_by_index(a, i+1), json_variant_by_index(b, j+1))) { | |
1403 | /* Key and values match! */ | |
1404 | key_b->is_marked = found = true; | |
1405 | ||
1406 | /* In the first iteration we continue the inner loop since we want to mark | |
1407 | * everything, otherwise exit the loop quickly after we found what we were | |
1408 | * looking for. */ | |
1409 | if (i != 0) | |
1410 | break; | |
1411 | } | |
1412 | } | |
1413 | ||
1414 | if (!found) | |
1415 | return false; | |
1416 | } | |
1417 | ||
1418 | return true; | |
1419 | } | |
1420 | ||
1421 | default: | |
04499a70 | 1422 | assert_not_reached(); |
cd0b6c53 LP |
1423 | } |
1424 | } | |
1425 | ||
83bc6cb7 LP |
1426 | void json_variant_sensitive(JsonVariant *v) { |
1427 | assert(v); | |
1428 | ||
1429 | /* Marks a variant as "sensitive", so that it is erased from memory when it is destroyed. This is a | |
1430 | * one-way operation: as soon as it is marked this way it remains marked this way until it's | |
162392b7 | 1431 | * destroyed. A magic variant is never sensitive though, even when asked, since it's too |
83bc6cb7 LP |
1432 | * basic. Similar, const string variant are never sensitive either, after all they are included in |
1433 | * the source code as they are, which is not suitable for inclusion of secrets. | |
1434 | * | |
1435 | * Note that this flag has a recursive effect: when we destroy an object or array we'll propagate the | |
1436 | * flag to all contained variants. And if those are then destroyed this is propagated further down, | |
1437 | * and so on. */ | |
1438 | ||
b7fc90a2 | 1439 | v = json_variant_formalize(v); |
83bc6cb7 LP |
1440 | if (!json_variant_is_regular(v)) |
1441 | return; | |
1442 | ||
1443 | v->sensitive = true; | |
1444 | } | |
1445 | ||
94600eeb LP |
1446 | bool json_variant_is_sensitive(JsonVariant *v) { |
1447 | v = json_variant_formalize(v); | |
1448 | if (!json_variant_is_regular(v)) | |
1449 | return false; | |
1450 | ||
1451 | return v->sensitive; | |
1452 | } | |
1453 | ||
1454 | static void json_variant_propagate_sensitive(JsonVariant *from, JsonVariant *to) { | |
1455 | if (json_variant_is_sensitive(from)) | |
1456 | json_variant_sensitive(to); | |
1457 | } | |
1458 | ||
cd0b6c53 LP |
1459 | int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column) { |
1460 | assert_return(v, -EINVAL); | |
1461 | ||
1462 | if (ret_source) | |
d520d519 | 1463 | *ret_source = json_variant_is_regular(v) && v->source ? v->source->name : NULL; |
cd0b6c53 LP |
1464 | |
1465 | if (ret_line) | |
d520d519 | 1466 | *ret_line = json_variant_is_regular(v) ? v->line : 0; |
cd0b6c53 LP |
1467 | |
1468 | if (ret_column) | |
d520d519 | 1469 | *ret_column = json_variant_is_regular(v) ? v->column : 0; |
cd0b6c53 LP |
1470 | |
1471 | return 0; | |
1472 | } | |
1473 | ||
897f099b | 1474 | static int print_source(FILE *f, JsonVariant *v, JsonFormatFlags flags, bool whitespace) { |
cd0b6c53 LP |
1475 | size_t w, k; |
1476 | ||
1477 | if (!FLAGS_SET(flags, JSON_FORMAT_SOURCE|JSON_FORMAT_PRETTY)) | |
1478 | return 0; | |
1479 | ||
d520d519 | 1480 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
1481 | return 0; |
1482 | ||
1483 | if (!v->source && v->line == 0 && v->column == 0) | |
1484 | return 0; | |
1485 | ||
1486 | /* The max width we need to format the line numbers for this source file */ | |
1487 | w = (v->source && v->source->max_line > 0) ? | |
1488 | DECIMAL_STR_WIDTH(v->source->max_line) : | |
1489 | DECIMAL_STR_MAX(unsigned)-1; | |
1490 | k = (v->source && v->source->max_column > 0) ? | |
1491 | DECIMAL_STR_WIDTH(v->source->max_column) : | |
1492 | DECIMAL_STR_MAX(unsigned) -1; | |
1493 | ||
1494 | if (whitespace) { | |
a4669764 ZJS |
1495 | size_t n = 1 + (v->source ? strlen(v->source->name) : 0) + |
1496 | ((v->source && (v->line > 0 || v->column > 0)) ? 1 : 0) + | |
1497 | (v->line > 0 ? w : 0) + | |
1498 | (((v->source || v->line > 0) && v->column > 0) ? 1 : 0) + | |
1499 | (v->column > 0 ? k : 0) + | |
1500 | 2; | |
1501 | ||
1502 | for (size_t i = 0; i < n; i++) | |
cd0b6c53 LP |
1503 | fputc(' ', f); |
1504 | } else { | |
1505 | fputc('[', f); | |
1506 | ||
1507 | if (v->source) | |
1508 | fputs(v->source->name, f); | |
1509 | if (v->source && (v->line > 0 || v->column > 0)) | |
1510 | fputc(':', f); | |
1511 | if (v->line > 0) | |
1512 | fprintf(f, "%*u", (int) w, v->line); | |
1513 | if ((v->source || v->line > 0) || v->column > 0) | |
1514 | fputc(':', f); | |
1515 | if (v->column > 0) | |
1516 | fprintf(f, "%*u", (int) k, v->column); | |
1517 | ||
1518 | fputc(']', f); | |
1519 | fputc(' ', f); | |
1520 | } | |
1521 | ||
1522 | return 0; | |
1523 | } | |
1524 | ||
e12b6e19 ZJS |
1525 | static void json_format_string(FILE *f, const char *q, JsonFormatFlags flags) { |
1526 | assert(q); | |
1527 | ||
1528 | fputc('"', f); | |
1529 | ||
1530 | if (flags & JSON_FORMAT_COLOR) | |
25e4608b | 1531 | fputs(ansi_green(), f); |
e12b6e19 ZJS |
1532 | |
1533 | for (; *q; q++) | |
1534 | switch (*q) { | |
1535 | case '"': | |
1536 | fputs("\\\"", f); | |
1537 | break; | |
1538 | ||
1539 | case '\\': | |
1540 | fputs("\\\\", f); | |
1541 | break; | |
1542 | ||
1543 | case '\b': | |
1544 | fputs("\\b", f); | |
1545 | break; | |
1546 | ||
1547 | case '\f': | |
1548 | fputs("\\f", f); | |
1549 | break; | |
1550 | ||
1551 | case '\n': | |
1552 | fputs("\\n", f); | |
1553 | break; | |
1554 | ||
1555 | case '\r': | |
1556 | fputs("\\r", f); | |
1557 | break; | |
1558 | ||
1559 | case '\t': | |
1560 | fputs("\\t", f); | |
1561 | break; | |
1562 | ||
1563 | default: | |
1564 | if ((signed char) *q >= 0 && *q < ' ') | |
5570a097 | 1565 | fprintf(f, "\\u%04x", (unsigned) *q); |
e12b6e19 ZJS |
1566 | else |
1567 | fputc(*q, f); | |
1568 | break; | |
1569 | } | |
1570 | ||
1571 | if (flags & JSON_FORMAT_COLOR) | |
1572 | fputs(ANSI_NORMAL, f); | |
1573 | ||
1574 | fputc('"', f); | |
1575 | } | |
1576 | ||
897f099b | 1577 | static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const char *prefix) { |
cd0b6c53 LP |
1578 | int r; |
1579 | ||
1580 | assert(f); | |
1581 | assert(v); | |
1582 | ||
1583 | switch (json_variant_type(v)) { | |
1584 | ||
1585 | case JSON_VARIANT_REAL: { | |
93258c7d | 1586 | locale_t loc, old_loc; |
cd0b6c53 LP |
1587 | |
1588 | loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); | |
1589 | if (loc == (locale_t) 0) | |
1590 | return -errno; | |
1591 | ||
1592 | if (flags & JSON_FORMAT_COLOR) | |
25e4608b | 1593 | fputs(ansi_highlight_blue(), f); |
cd0b6c53 | 1594 | |
93258c7d | 1595 | old_loc = uselocale(loc); |
337712e7 | 1596 | fprintf(f, "%.*e", DECIMAL_DIG, json_variant_real(v)); |
93258c7d | 1597 | uselocale(old_loc); |
cd0b6c53 LP |
1598 | |
1599 | if (flags & JSON_FORMAT_COLOR) | |
1600 | fputs(ANSI_NORMAL, f); | |
1601 | ||
1602 | freelocale(loc); | |
1603 | break; | |
1604 | } | |
1605 | ||
1606 | case JSON_VARIANT_INTEGER: | |
1607 | if (flags & JSON_FORMAT_COLOR) | |
25e4608b | 1608 | fputs(ansi_highlight_blue(), f); |
cd0b6c53 LP |
1609 | |
1610 | fprintf(f, "%" PRIdMAX, json_variant_integer(v)); | |
1611 | ||
1612 | if (flags & JSON_FORMAT_COLOR) | |
1613 | fputs(ANSI_NORMAL, f); | |
1614 | break; | |
1615 | ||
1616 | case JSON_VARIANT_UNSIGNED: | |
1617 | if (flags & JSON_FORMAT_COLOR) | |
25e4608b | 1618 | fputs(ansi_highlight_blue(), f); |
cd0b6c53 LP |
1619 | |
1620 | fprintf(f, "%" PRIuMAX, json_variant_unsigned(v)); | |
1621 | ||
1622 | if (flags & JSON_FORMAT_COLOR) | |
1623 | fputs(ANSI_NORMAL, f); | |
1624 | break; | |
1625 | ||
1626 | case JSON_VARIANT_BOOLEAN: | |
1627 | ||
1628 | if (flags & JSON_FORMAT_COLOR) | |
1629 | fputs(ANSI_HIGHLIGHT, f); | |
1630 | ||
1631 | if (json_variant_boolean(v)) | |
1632 | fputs("true", f); | |
1633 | else | |
1634 | fputs("false", f); | |
1635 | ||
1636 | if (flags & JSON_FORMAT_COLOR) | |
1637 | fputs(ANSI_NORMAL, f); | |
1638 | ||
1639 | break; | |
1640 | ||
1641 | case JSON_VARIANT_NULL: | |
1642 | if (flags & JSON_FORMAT_COLOR) | |
1643 | fputs(ANSI_HIGHLIGHT, f); | |
1644 | ||
1645 | fputs("null", f); | |
1646 | ||
1647 | if (flags & JSON_FORMAT_COLOR) | |
1648 | fputs(ANSI_NORMAL, f); | |
1649 | break; | |
1650 | ||
e12b6e19 ZJS |
1651 | case JSON_VARIANT_STRING: |
1652 | json_format_string(f, json_variant_string(v), flags); | |
cd0b6c53 | 1653 | break; |
cd0b6c53 LP |
1654 | |
1655 | case JSON_VARIANT_ARRAY: { | |
a4669764 | 1656 | size_t n = json_variant_elements(v); |
cd0b6c53 LP |
1657 | if (n == 0) |
1658 | fputs("[]", f); | |
1659 | else { | |
4ae7e4e5 | 1660 | _cleanup_free_ char *joined = NULL; |
cd0b6c53 LP |
1661 | const char *prefix2; |
1662 | ||
1663 | if (flags & JSON_FORMAT_PRETTY) { | |
4ae7e4e5 LP |
1664 | joined = strjoin(strempty(prefix), "\t"); |
1665 | if (!joined) | |
1666 | return -ENOMEM; | |
1667 | ||
1668 | prefix2 = joined; | |
cd0b6c53 LP |
1669 | fputs("[\n", f); |
1670 | } else { | |
1671 | prefix2 = strempty(prefix); | |
1672 | fputc('[', f); | |
1673 | } | |
1674 | ||
a4669764 | 1675 | for (size_t i = 0; i < n; i++) { |
cd0b6c53 LP |
1676 | JsonVariant *e; |
1677 | ||
1678 | assert_se(e = json_variant_by_index(v, i)); | |
1679 | ||
1680 | if (i > 0) { | |
1681 | if (flags & JSON_FORMAT_PRETTY) | |
1682 | fputs(",\n", f); | |
1683 | else | |
1684 | fputc(',', f); | |
1685 | } | |
1686 | ||
1687 | if (flags & JSON_FORMAT_PRETTY) { | |
1688 | print_source(f, e, flags, false); | |
1689 | fputs(prefix2, f); | |
1690 | } | |
1691 | ||
1692 | r = json_format(f, e, flags, prefix2); | |
1693 | if (r < 0) | |
1694 | return r; | |
1695 | } | |
1696 | ||
1697 | if (flags & JSON_FORMAT_PRETTY) { | |
1698 | fputc('\n', f); | |
1699 | print_source(f, v, flags, true); | |
1700 | fputs(strempty(prefix), f); | |
1701 | } | |
1702 | ||
1703 | fputc(']', f); | |
1704 | } | |
1705 | break; | |
1706 | } | |
1707 | ||
1708 | case JSON_VARIANT_OBJECT: { | |
a4669764 | 1709 | size_t n = json_variant_elements(v); |
cd0b6c53 LP |
1710 | if (n == 0) |
1711 | fputs("{}", f); | |
1712 | else { | |
4ae7e4e5 | 1713 | _cleanup_free_ char *joined = NULL; |
cd0b6c53 LP |
1714 | const char *prefix2; |
1715 | ||
1716 | if (flags & JSON_FORMAT_PRETTY) { | |
4ae7e4e5 LP |
1717 | joined = strjoin(strempty(prefix), "\t"); |
1718 | if (!joined) | |
1719 | return -ENOMEM; | |
1720 | ||
1721 | prefix2 = joined; | |
cd0b6c53 LP |
1722 | fputs("{\n", f); |
1723 | } else { | |
1724 | prefix2 = strempty(prefix); | |
1725 | fputc('{', f); | |
1726 | } | |
1727 | ||
a4669764 | 1728 | for (size_t i = 0; i < n; i += 2) { |
cd0b6c53 LP |
1729 | JsonVariant *e; |
1730 | ||
1731 | e = json_variant_by_index(v, i); | |
1732 | ||
1733 | if (i > 0) { | |
1734 | if (flags & JSON_FORMAT_PRETTY) | |
1735 | fputs(",\n", f); | |
1736 | else | |
1737 | fputc(',', f); | |
1738 | } | |
1739 | ||
1740 | if (flags & JSON_FORMAT_PRETTY) { | |
1741 | print_source(f, e, flags, false); | |
1742 | fputs(prefix2, f); | |
1743 | } | |
1744 | ||
1745 | r = json_format(f, e, flags, prefix2); | |
1746 | if (r < 0) | |
1747 | return r; | |
1748 | ||
1749 | fputs(flags & JSON_FORMAT_PRETTY ? " : " : ":", f); | |
1750 | ||
1751 | r = json_format(f, json_variant_by_index(v, i+1), flags, prefix2); | |
1752 | if (r < 0) | |
1753 | return r; | |
1754 | } | |
1755 | ||
1756 | if (flags & JSON_FORMAT_PRETTY) { | |
1757 | fputc('\n', f); | |
1758 | print_source(f, v, flags, true); | |
1759 | fputs(strempty(prefix), f); | |
1760 | } | |
1761 | ||
1762 | fputc('}', f); | |
1763 | } | |
1764 | break; | |
1765 | } | |
1766 | ||
1767 | default: | |
04499a70 | 1768 | assert_not_reached(); |
cd0b6c53 LP |
1769 | } |
1770 | ||
1771 | return 0; | |
1772 | } | |
1773 | ||
fa9a6db4 LB |
1774 | static bool json_variant_is_sensitive_recursive(JsonVariant *v) { |
1775 | if (!v) | |
1776 | return false; | |
1777 | if (json_variant_is_sensitive(v)) | |
1778 | return true; | |
fa9a6db4 LB |
1779 | if (!json_variant_is_regular(v)) |
1780 | return false; | |
1781 | if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) | |
1782 | return false; | |
1783 | if (v->is_reference) | |
1784 | return json_variant_is_sensitive_recursive(v->reference); | |
1785 | ||
1786 | for (size_t i = 0; i < json_variant_elements(v); i++) | |
1787 | if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i))) | |
1788 | return true; | |
1789 | ||
1790 | return false; | |
1791 | } | |
1792 | ||
897f099b | 1793 | int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) { |
2485b7e2 YW |
1794 | _cleanup_(memstream_done) MemStream m = {}; |
1795 | size_t sz; | |
1796 | FILE *f; | |
cd0b6c53 LP |
1797 | int r; |
1798 | ||
2a04712c ZJS |
1799 | /* Returns the length of the generated string (without the terminating NUL), |
1800 | * or negative on error. */ | |
1801 | ||
cd0b6c53 LP |
1802 | assert_return(v, -EINVAL); |
1803 | assert_return(ret, -EINVAL); | |
1804 | ||
10d71263 LP |
1805 | if (flags & JSON_FORMAT_OFF) |
1806 | return -ENOEXEC; | |
1807 | ||
fa9a6db4 LB |
1808 | if ((flags & JSON_FORMAT_REFUSE_SENSITIVE)) |
1809 | if (json_variant_is_sensitive_recursive(v)) | |
1810 | return -EPERM; | |
1811 | ||
2485b7e2 | 1812 | f = memstream_init(&m); |
f392dfb5 FS |
1813 | if (!f) |
1814 | return -ENOMEM; | |
cd0b6c53 | 1815 | |
f392dfb5 FS |
1816 | r = json_variant_dump(v, flags, f, NULL); |
1817 | if (r < 0) | |
1818 | return r; | |
cd0b6c53 | 1819 | |
2485b7e2 | 1820 | r = memstream_finalize(&m, ret, &sz); |
cd0b6c53 LP |
1821 | if (r < 0) |
1822 | return r; | |
1823 | ||
2485b7e2 | 1824 | return sz; |
cd0b6c53 LP |
1825 | } |
1826 | ||
7922ead5 | 1827 | int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) { |
c3f0bff9 ZJS |
1828 | if (!v) { |
1829 | if (flags & JSON_FORMAT_EMPTY_ARRAY) | |
1830 | v = JSON_VARIANT_MAGIC_EMPTY_ARRAY; | |
1831 | else | |
1832 | return 0; | |
1833 | } | |
cd0b6c53 LP |
1834 | |
1835 | if (!f) | |
1836 | f = stdout; | |
1837 | ||
1838 | print_source(f, v, flags, false); | |
1839 | ||
ab91733c LP |
1840 | if (((flags & (JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_COLOR)) == JSON_FORMAT_COLOR_AUTO) && colors_enabled()) |
1841 | flags |= JSON_FORMAT_COLOR; | |
1842 | ||
2d814304 LP |
1843 | if (((flags & (JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_PRETTY)) == JSON_FORMAT_PRETTY_AUTO)) |
1844 | flags |= on_tty() ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE; | |
1845 | ||
cd0b6c53 LP |
1846 | if (flags & JSON_FORMAT_SSE) |
1847 | fputs("data: ", f); | |
1848 | if (flags & JSON_FORMAT_SEQ) | |
1849 | fputc('\x1e', f); /* ASCII Record Separator */ | |
1850 | ||
1851 | json_format(f, v, flags, prefix); | |
1852 | ||
1853 | if (flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_SEQ|JSON_FORMAT_SSE|JSON_FORMAT_NEWLINE)) | |
1854 | fputc('\n', f); | |
1855 | if (flags & JSON_FORMAT_SSE) | |
1856 | fputc('\n', f); /* In case of SSE add a second newline */ | |
0b1f2e8a LP |
1857 | |
1858 | if (flags & JSON_FORMAT_FLUSH) | |
7922ead5 ZJS |
1859 | return fflush_and_check(f); |
1860 | return 0; | |
cd0b6c53 LP |
1861 | } |
1862 | ||
f2ff34ff LP |
1863 | int json_variant_filter(JsonVariant **v, char **to_remove) { |
1864 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; | |
1865 | _cleanup_free_ JsonVariant **array = NULL; | |
a4669764 | 1866 | size_t n = 0, k = 0; |
f2ff34ff LP |
1867 | int r; |
1868 | ||
1869 | assert(v); | |
1870 | ||
1871 | if (json_variant_is_blank_object(*v)) | |
1872 | return 0; | |
1873 | if (!json_variant_is_object(*v)) | |
1874 | return -EINVAL; | |
1875 | ||
1876 | if (strv_isempty(to_remove)) | |
1877 | return 0; | |
1878 | ||
a4669764 | 1879 | for (size_t i = 0; i < json_variant_elements(*v); i += 2) { |
f2ff34ff LP |
1880 | JsonVariant *p; |
1881 | ||
1882 | p = json_variant_by_index(*v, i); | |
1883 | if (!json_variant_has_type(p, JSON_VARIANT_STRING)) | |
1884 | return -EINVAL; | |
1885 | ||
1886 | if (strv_contains(to_remove, json_variant_string(p))) { | |
1887 | if (!array) { | |
1888 | array = new(JsonVariant*, json_variant_elements(*v) - 2); | |
1889 | if (!array) | |
1890 | return -ENOMEM; | |
1891 | ||
1892 | for (k = 0; k < i; k++) | |
1893 | array[k] = json_variant_by_index(*v, k); | |
1894 | } | |
1895 | ||
1896 | n++; | |
1897 | } else if (array) { | |
1898 | array[k++] = p; | |
1899 | array[k++] = json_variant_by_index(*v, i + 1); | |
1900 | } | |
1901 | } | |
1902 | ||
1903 | if (n == 0) | |
1904 | return 0; | |
1905 | ||
1906 | r = json_variant_new_object(&w, array, k); | |
1907 | if (r < 0) | |
1908 | return r; | |
1909 | ||
94600eeb | 1910 | json_variant_propagate_sensitive(*v, w); |
ce913e0e | 1911 | JSON_VARIANT_REPLACE(*v, TAKE_PTR(w)); |
f2ff34ff LP |
1912 | |
1913 | return (int) n; | |
1914 | } | |
1915 | ||
78a41236 LP |
1916 | int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value) { |
1917 | _cleanup_(json_variant_unrefp) JsonVariant *field_variant = NULL, *w = NULL; | |
1918 | _cleanup_free_ JsonVariant **array = NULL; | |
a4669764 | 1919 | size_t k = 0; |
78a41236 LP |
1920 | int r; |
1921 | ||
1922 | assert(v); | |
1923 | assert(field); | |
1924 | ||
1925 | if (json_variant_is_blank_object(*v)) { | |
1926 | array = new(JsonVariant*, 2); | |
1927 | if (!array) | |
1928 | return -ENOMEM; | |
1929 | ||
1930 | } else { | |
1931 | if (!json_variant_is_object(*v)) | |
1932 | return -EINVAL; | |
1933 | ||
a4669764 | 1934 | for (size_t i = 0; i < json_variant_elements(*v); i += 2) { |
78a41236 LP |
1935 | JsonVariant *p; |
1936 | ||
1937 | p = json_variant_by_index(*v, i); | |
1938 | if (!json_variant_is_string(p)) | |
1939 | return -EINVAL; | |
1940 | ||
1941 | if (streq(json_variant_string(p), field)) { | |
1942 | ||
1943 | if (!array) { | |
1944 | array = new(JsonVariant*, json_variant_elements(*v)); | |
1945 | if (!array) | |
1946 | return -ENOMEM; | |
1947 | ||
1948 | for (k = 0; k < i; k++) | |
1949 | array[k] = json_variant_by_index(*v, k); | |
1950 | } | |
1951 | ||
1952 | } else if (array) { | |
1953 | array[k++] = p; | |
1954 | array[k++] = json_variant_by_index(*v, i + 1); | |
1955 | } | |
1956 | } | |
1957 | ||
1958 | if (!array) { | |
1959 | array = new(JsonVariant*, json_variant_elements(*v) + 2); | |
1960 | if (!array) | |
1961 | return -ENOMEM; | |
1962 | ||
1963 | for (k = 0; k < json_variant_elements(*v); k++) | |
1964 | array[k] = json_variant_by_index(*v, k); | |
1965 | } | |
1966 | } | |
1967 | ||
1968 | r = json_variant_new_string(&field_variant, field); | |
1969 | if (r < 0) | |
1970 | return r; | |
1971 | ||
1972 | array[k++] = field_variant; | |
1973 | array[k++] = value; | |
1974 | ||
1975 | r = json_variant_new_object(&w, array, k); | |
1976 | if (r < 0) | |
1977 | return r; | |
1978 | ||
94600eeb | 1979 | json_variant_propagate_sensitive(*v, w); |
ce913e0e | 1980 | JSON_VARIANT_REPLACE(*v, TAKE_PTR(w)); |
78a41236 LP |
1981 | |
1982 | return 1; | |
1983 | } | |
1984 | ||
f5fc7732 LP |
1985 | int json_variant_set_fieldb(JsonVariant **v, const char *field, ...) { |
1986 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; | |
1987 | va_list ap; | |
1988 | int r; | |
1989 | ||
1990 | va_start(ap, field); | |
1991 | r = json_buildv(&w, ap); | |
1992 | va_end(ap); | |
1993 | if (r < 0) | |
1994 | return r; | |
1995 | ||
1996 | return json_variant_set_field(v, field, w); | |
1997 | } | |
1998 | ||
15f1fb3e LP |
1999 | int json_variant_set_field_string(JsonVariant **v, const char *field, const char *value) { |
2000 | _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; | |
2001 | int r; | |
2002 | ||
2003 | r = json_variant_new_string(&m, value); | |
2004 | if (r < 0) | |
2005 | return r; | |
2006 | ||
2007 | return json_variant_set_field(v, field, m); | |
2008 | } | |
2009 | ||
718ca772 | 2010 | int json_variant_set_field_integer(JsonVariant **v, const char *field, int64_t i) { |
a832b08e LP |
2011 | _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; |
2012 | int r; | |
2013 | ||
2014 | r = json_variant_new_integer(&m, i); | |
2015 | if (r < 0) | |
2016 | return r; | |
2017 | ||
2018 | return json_variant_set_field(v, field, m); | |
2019 | } | |
2020 | ||
718ca772 | 2021 | int json_variant_set_field_unsigned(JsonVariant **v, const char *field, uint64_t u) { |
15f1fb3e LP |
2022 | _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; |
2023 | int r; | |
2024 | ||
2025 | r = json_variant_new_unsigned(&m, u); | |
2026 | if (r < 0) | |
2027 | return r; | |
2028 | ||
2029 | return json_variant_set_field(v, field, m); | |
2030 | } | |
2031 | ||
a832b08e LP |
2032 | int json_variant_set_field_boolean(JsonVariant **v, const char *field, bool b) { |
2033 | _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; | |
2034 | int r; | |
2035 | ||
2036 | r = json_variant_new_boolean(&m, b); | |
2037 | if (r < 0) | |
2038 | return r; | |
2039 | ||
2040 | return json_variant_set_field(v, field, m); | |
2041 | } | |
2042 | ||
0b9481cf LP |
2043 | int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l) { |
2044 | _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; | |
2045 | int r; | |
2046 | ||
2047 | r = json_variant_new_array_strv(&m, l); | |
2048 | if (r < 0) | |
2049 | return r; | |
2050 | ||
2051 | return json_variant_set_field(v, field, m); | |
2052 | } | |
2053 | ||
e931768e | 2054 | int json_variant_merge_object(JsonVariant **v, JsonVariant *m) { |
ca409a59 LP |
2055 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; |
2056 | _cleanup_free_ JsonVariant **array = NULL; | |
a4669764 | 2057 | size_t v_elements, m_elements, k; |
ca409a59 LP |
2058 | bool v_blank, m_blank; |
2059 | int r; | |
2060 | ||
2061 | m = json_variant_dereference(m); | |
2062 | ||
2063 | v_blank = json_variant_is_blank_object(*v); | |
2064 | m_blank = json_variant_is_blank_object(m); | |
2065 | ||
2066 | if (!v_blank && !json_variant_is_object(*v)) | |
2067 | return -EINVAL; | |
2068 | if (!m_blank && !json_variant_is_object(m)) | |
2069 | return -EINVAL; | |
2070 | ||
2071 | if (m_blank) | |
2072 | return 0; /* nothing to do */ | |
2073 | ||
2074 | if (v_blank) { | |
ce913e0e | 2075 | JSON_VARIANT_REPLACE(*v, json_variant_ref(m)); |
ca409a59 LP |
2076 | return 1; |
2077 | } | |
2078 | ||
2079 | v_elements = json_variant_elements(*v); | |
2080 | m_elements = json_variant_elements(m); | |
2081 | if (v_elements > SIZE_MAX - m_elements) /* overflow check */ | |
2082 | return -ENOMEM; | |
2083 | ||
2084 | array = new(JsonVariant*, v_elements + m_elements); | |
2085 | if (!array) | |
2086 | return -ENOMEM; | |
2087 | ||
2088 | k = 0; | |
a4669764 | 2089 | for (size_t i = 0; i < v_elements; i += 2) { |
ca409a59 LP |
2090 | JsonVariant *u; |
2091 | ||
2092 | u = json_variant_by_index(*v, i); | |
2093 | if (!json_variant_is_string(u)) | |
2094 | return -EINVAL; | |
2095 | ||
2096 | if (json_variant_by_key(m, json_variant_string(u))) | |
2097 | continue; /* skip if exists in second variant */ | |
2098 | ||
2099 | array[k++] = u; | |
2100 | array[k++] = json_variant_by_index(*v, i + 1); | |
2101 | } | |
2102 | ||
a4669764 | 2103 | for (size_t i = 0; i < m_elements; i++) |
ca409a59 LP |
2104 | array[k++] = json_variant_by_index(m, i); |
2105 | ||
2106 | r = json_variant_new_object(&w, array, k); | |
2107 | if (r < 0) | |
2108 | return r; | |
2109 | ||
94600eeb LP |
2110 | json_variant_propagate_sensitive(*v, w); |
2111 | json_variant_propagate_sensitive(m, w); | |
ce913e0e | 2112 | JSON_VARIANT_REPLACE(*v, TAKE_PTR(w)); |
ca409a59 LP |
2113 | |
2114 | return 1; | |
2115 | } | |
2116 | ||
e931768e LP |
2117 | int json_variant_merge_objectb(JsonVariant **v, ...) { |
2118 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; | |
2119 | va_list ap; | |
2120 | int r; | |
2121 | ||
2122 | va_start(ap, v); | |
2123 | r = json_buildv(&w, ap); | |
2124 | va_end(ap); | |
2125 | if (r < 0) | |
2126 | return r; | |
2127 | ||
2128 | return json_variant_merge_object(v, w); | |
2129 | } | |
2130 | ||
21e21511 LP |
2131 | int json_variant_append_array(JsonVariant **v, JsonVariant *element) { |
2132 | _cleanup_(json_variant_unrefp) JsonVariant *nv = NULL; | |
2133 | bool blank; | |
2134 | int r; | |
2135 | ||
2136 | assert(v); | |
2137 | assert(element); | |
2138 | ||
21e21511 LP |
2139 | if (!*v || json_variant_is_null(*v)) |
2140 | blank = true; | |
8525bb36 | 2141 | else if (json_variant_is_array(*v)) |
21e21511 | 2142 | blank = json_variant_elements(*v) == 0; |
8525bb36 ZJS |
2143 | else |
2144 | return -EINVAL; | |
21e21511 | 2145 | |
8525bb36 | 2146 | if (blank) { |
21e21511 | 2147 | r = json_variant_new_array(&nv, (JsonVariant*[]) { element }, 1); |
8525bb36 ZJS |
2148 | if (r < 0) |
2149 | return r; | |
2150 | } else if (json_variant_n_ref(*v) == 1) { | |
2151 | /* Let's bump the reference count on element. We can't do the realloc if we're appending *v | |
2152 | * to itself, or one of the objects embedded in *v to *v. If the reference count grows, we | |
2153 | * need to fall back to the other method below. */ | |
2154 | ||
2155 | _unused_ _cleanup_(json_variant_unrefp) JsonVariant *dummy = json_variant_ref(element); | |
2156 | if (json_variant_n_ref(*v) == 1) { | |
2157 | /* We hold the only reference. Let's mutate the object. */ | |
2158 | size_t size = json_variant_elements(*v); | |
2159 | void *old = *v; | |
2160 | ||
2161 | if (!GREEDY_REALLOC(*v, size + 1 + 1)) | |
2162 | return -ENOMEM; | |
2163 | ||
2164 | if (old != *v) | |
2165 | /* Readjust the parent pointers to the new address */ | |
2166 | for (size_t i = 1; i < size; i++) | |
2167 | (*v)[1 + i].parent = *v; | |
2168 | ||
2169 | return _json_variant_array_put_element(*v, element); | |
2170 | } | |
2171 | } | |
2172 | ||
2173 | if (!blank) { | |
2174 | size_t size = json_variant_elements(*v); | |
2175 | ||
2176 | _cleanup_free_ JsonVariant **array = new(JsonVariant*, size + 1); | |
21e21511 LP |
2177 | if (!array) |
2178 | return -ENOMEM; | |
2179 | ||
a4669764 | 2180 | for (size_t i = 0; i < size; i++) |
21e21511 LP |
2181 | array[i] = json_variant_by_index(*v, i); |
2182 | ||
a4669764 | 2183 | array[size] = element; |
21e21511 | 2184 | |
a4669764 | 2185 | r = json_variant_new_array(&nv, array, size + 1); |
8525bb36 ZJS |
2186 | if (r < 0) |
2187 | return r; | |
21e21511 | 2188 | } |
21e21511 | 2189 | |
94600eeb | 2190 | json_variant_propagate_sensitive(*v, nv); |
ce913e0e | 2191 | JSON_VARIANT_REPLACE(*v, TAKE_PTR(nv)); |
21e21511 LP |
2192 | |
2193 | return 0; | |
2194 | } | |
2195 | ||
3218794f LP |
2196 | int json_variant_append_arrayb(JsonVariant **v, ...) { |
2197 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; | |
2198 | va_list ap; | |
2199 | int r; | |
2200 | ||
2201 | va_start(ap, v); | |
2202 | r = json_buildv(&w, ap); | |
2203 | va_end(ap); | |
2204 | if (r < 0) | |
2205 | return r; | |
2206 | ||
2207 | return json_variant_append_array(v, w); | |
2208 | } | |
2209 | ||
3bb326c5 LP |
2210 | JsonVariant *json_variant_find(JsonVariant *haystack, JsonVariant *needle) { |
2211 | JsonVariant *i; | |
2212 | ||
2213 | /* Find a json object in an array. Returns NULL if not found, or if the array is not actually an array. */ | |
2214 | ||
2215 | JSON_VARIANT_ARRAY_FOREACH(i, haystack) | |
2216 | if (json_variant_equal(i, needle)) | |
2217 | return i; | |
2218 | ||
2219 | return NULL; | |
2220 | } | |
2221 | ||
2222 | int json_variant_append_array_nodup(JsonVariant **v, JsonVariant *element) { | |
2223 | assert(v); | |
2224 | ||
2225 | if (json_variant_find(*v, element)) | |
2226 | return 0; | |
2227 | ||
2228 | return json_variant_append_array(v, element); | |
2229 | } | |
2230 | ||
22f14d6b LP |
2231 | int json_variant_strv(JsonVariant *v, char ***ret) { |
2232 | char **l = NULL; | |
22f14d6b LP |
2233 | bool sensitive; |
2234 | int r; | |
2235 | ||
2236 | assert(ret); | |
2237 | ||
2238 | if (!v || json_variant_is_null(v)) { | |
2239 | l = new0(char*, 1); | |
2240 | if (!l) | |
2241 | return -ENOMEM; | |
2242 | ||
2243 | *ret = l; | |
2244 | return 0; | |
2245 | } | |
2246 | ||
2247 | if (!json_variant_is_array(v)) | |
2248 | return -EINVAL; | |
2249 | ||
909eb4c0 | 2250 | sensitive = json_variant_is_sensitive(v); |
22f14d6b | 2251 | |
a4669764 | 2252 | size_t n = json_variant_elements(v); |
22f14d6b LP |
2253 | l = new(char*, n+1); |
2254 | if (!l) | |
2255 | return -ENOMEM; | |
2256 | ||
a4669764 | 2257 | for (size_t i = 0; i < n; i++) { |
22f14d6b LP |
2258 | JsonVariant *e; |
2259 | ||
2260 | assert_se(e = json_variant_by_index(v, i)); | |
909eb4c0 | 2261 | sensitive = sensitive || json_variant_is_sensitive(e); |
22f14d6b LP |
2262 | |
2263 | if (!json_variant_is_string(e)) { | |
2264 | l[i] = NULL; | |
2265 | r = -EINVAL; | |
2266 | goto fail; | |
2267 | } | |
2268 | ||
2269 | l[i] = strdup(json_variant_string(e)); | |
2270 | if (!l[i]) { | |
2271 | r = -ENOMEM; | |
2272 | goto fail; | |
2273 | } | |
2274 | } | |
2275 | ||
a4669764 | 2276 | l[n] = NULL; |
22f14d6b LP |
2277 | *ret = TAKE_PTR(l); |
2278 | ||
2279 | return 0; | |
2280 | ||
2281 | fail: | |
2282 | if (sensitive) | |
2283 | strv_free_erase(l); | |
2284 | else | |
2285 | strv_free(l); | |
2286 | ||
2287 | return r; | |
2288 | } | |
2289 | ||
cd0b6c53 LP |
2290 | static int json_variant_copy(JsonVariant **nv, JsonVariant *v) { |
2291 | JsonVariantType t; | |
2292 | JsonVariant *c; | |
2293 | JsonValue value; | |
2294 | const void *source; | |
2295 | size_t k; | |
2296 | ||
2297 | assert(nv); | |
2298 | assert(v); | |
2299 | ||
2300 | /* Let's copy the simple types literally, and the larger types by references */ | |
2301 | t = json_variant_type(v); | |
2302 | switch (t) { | |
2303 | case JSON_VARIANT_INTEGER: | |
718ca772 | 2304 | k = sizeof(int64_t); |
cd0b6c53 LP |
2305 | value.integer = json_variant_integer(v); |
2306 | source = &value; | |
2307 | break; | |
2308 | ||
2309 | case JSON_VARIANT_UNSIGNED: | |
718ca772 | 2310 | k = sizeof(uint64_t); |
cd0b6c53 LP |
2311 | value.unsig = json_variant_unsigned(v); |
2312 | source = &value; | |
2313 | break; | |
2314 | ||
2315 | case JSON_VARIANT_REAL: | |
337712e7 | 2316 | k = sizeof(double); |
cd0b6c53 LP |
2317 | value.real = json_variant_real(v); |
2318 | source = &value; | |
2319 | break; | |
2320 | ||
2321 | case JSON_VARIANT_BOOLEAN: | |
2322 | k = sizeof(bool); | |
2323 | value.boolean = json_variant_boolean(v); | |
2324 | source = &value; | |
2325 | break; | |
2326 | ||
2327 | case JSON_VARIANT_NULL: | |
2328 | k = 0; | |
2329 | source = NULL; | |
2330 | break; | |
2331 | ||
2332 | case JSON_VARIANT_STRING: | |
2333 | source = json_variant_string(v); | |
2334 | k = strnlen(source, INLINE_STRING_MAX + 1); | |
2335 | if (k <= INLINE_STRING_MAX) { | |
b3a9d980 | 2336 | k++; |
cd0b6c53 LP |
2337 | break; |
2338 | } | |
2339 | ||
2340 | _fallthrough_; | |
2341 | ||
2342 | default: | |
2343 | /* Everything else copy by reference */ | |
2344 | ||
2eb1c198 LP |
2345 | c = malloc0(MAX(sizeof(JsonVariant), |
2346 | offsetof(JsonVariant, reference) + sizeof(JsonVariant*))); | |
cd0b6c53 LP |
2347 | if (!c) |
2348 | return -ENOMEM; | |
2349 | ||
2350 | c->n_ref = 1; | |
2351 | c->type = t; | |
2352 | c->is_reference = true; | |
b7fc90a2 | 2353 | c->reference = json_variant_ref(json_variant_formalize(v)); |
cd0b6c53 LP |
2354 | |
2355 | *nv = c; | |
2356 | return 0; | |
2357 | } | |
2358 | ||
2eb1c198 LP |
2359 | c = malloc0(MAX(sizeof(JsonVariant), |
2360 | offsetof(JsonVariant, value) + k)); | |
cd0b6c53 LP |
2361 | if (!c) |
2362 | return -ENOMEM; | |
2363 | ||
2364 | c->n_ref = 1; | |
2365 | c->type = t; | |
2366 | ||
2367 | memcpy_safe(&c->value, source, k); | |
2368 | ||
94600eeb LP |
2369 | json_variant_propagate_sensitive(v, c); |
2370 | ||
cd0b6c53 LP |
2371 | *nv = c; |
2372 | return 0; | |
2373 | } | |
2374 | ||
2375 | static bool json_single_ref(JsonVariant *v) { | |
2376 | ||
2377 | /* Checks whether the caller is the single owner of the object, i.e. can get away with changing it */ | |
2378 | ||
d520d519 | 2379 | if (!json_variant_is_regular(v)) |
cd0b6c53 LP |
2380 | return false; |
2381 | ||
2382 | if (v->is_embedded) | |
2383 | return json_single_ref(v->parent); | |
2384 | ||
2385 | assert(v->n_ref > 0); | |
2386 | return v->n_ref == 1; | |
2387 | } | |
2388 | ||
2389 | static int json_variant_set_source(JsonVariant **v, JsonSource *source, unsigned line, unsigned column) { | |
2390 | JsonVariant *w; | |
2391 | int r; | |
2392 | ||
2393 | assert(v); | |
2394 | ||
bac06497 ZJS |
2395 | /* Patch in source and line/column number. Tries to do this in-place if the caller is the sole |
2396 | * referencer of the object. If not, allocates a new object, possibly a surrogate for the original | |
2397 | * one */ | |
cd0b6c53 LP |
2398 | |
2399 | if (!*v) | |
2400 | return 0; | |
2401 | ||
2402 | if (source && line > source->max_line) | |
2403 | source->max_line = line; | |
2404 | if (source && column > source->max_column) | |
2405 | source->max_column = column; | |
2406 | ||
d520d519 | 2407 | if (!json_variant_is_regular(*v)) { |
cd0b6c53 LP |
2408 | |
2409 | if (!source && line == 0 && column == 0) | |
2410 | return 0; | |
2411 | ||
2412 | } else { | |
2413 | if (json_source_equal((*v)->source, source) && | |
2414 | (*v)->line == line && | |
2415 | (*v)->column == column) | |
2416 | return 0; | |
2417 | ||
2418 | if (json_single_ref(*v)) { /* Sole reference? */ | |
2419 | json_source_unref((*v)->source); | |
2420 | (*v)->source = json_source_ref(source); | |
2421 | (*v)->line = line; | |
2422 | (*v)->column = column; | |
2423 | return 1; | |
2424 | } | |
2425 | } | |
2426 | ||
2427 | r = json_variant_copy(&w, *v); | |
2428 | if (r < 0) | |
2429 | return r; | |
2430 | ||
d520d519 | 2431 | assert(json_variant_is_regular(w)); |
cd0b6c53 LP |
2432 | assert(!w->is_embedded); |
2433 | assert(w->n_ref == 1); | |
2434 | assert(!w->source); | |
2435 | ||
2436 | w->source = json_source_ref(source); | |
2437 | w->line = line; | |
2438 | w->column = column; | |
2439 | ||
ce913e0e | 2440 | JSON_VARIANT_REPLACE(*v, w); |
cd0b6c53 LP |
2441 | |
2442 | return 1; | |
2443 | } | |
2444 | ||
2445 | static void inc_lines_columns(unsigned *line, unsigned *column, const char *s, size_t n) { | |
2446 | assert(line); | |
2447 | assert(column); | |
2448 | assert(s || n == 0); | |
2449 | ||
2450 | while (n > 0) { | |
cd0b6c53 LP |
2451 | if (*s == '\n') { |
2452 | (*line)++; | |
2453 | *column = 1; | |
df7f9e0b | 2454 | } else if ((signed char) *s >= 0 && *s < 127) /* Process ASCII chars quickly */ |
cd0b6c53 LP |
2455 | (*column)++; |
2456 | else { | |
2457 | int w; | |
2458 | ||
92e068b4 | 2459 | w = utf8_encoded_valid_unichar(s, n); |
cd0b6c53 LP |
2460 | if (w < 0) /* count invalid unichars as normal characters */ |
2461 | w = 1; | |
2462 | else if ((size_t) w > n) /* never read more than the specified number of characters */ | |
2463 | w = (int) n; | |
2464 | ||
2465 | (*column)++; | |
2466 | ||
2467 | s += w; | |
2468 | n -= w; | |
2469 | continue; | |
2470 | } | |
2471 | ||
2472 | s++; | |
2473 | n--; | |
2474 | } | |
2475 | } | |
2476 | ||
2477 | static int unhex_ucs2(const char *c, uint16_t *ret) { | |
2478 | int aa, bb, cc, dd; | |
2479 | uint16_t x; | |
2480 | ||
2481 | assert(c); | |
2482 | assert(ret); | |
2483 | ||
2484 | aa = unhexchar(c[0]); | |
2485 | if (aa < 0) | |
2486 | return -EINVAL; | |
2487 | ||
2488 | bb = unhexchar(c[1]); | |
2489 | if (bb < 0) | |
2490 | return -EINVAL; | |
2491 | ||
2492 | cc = unhexchar(c[2]); | |
2493 | if (cc < 0) | |
2494 | return -EINVAL; | |
2495 | ||
2496 | dd = unhexchar(c[3]); | |
2497 | if (dd < 0) | |
2498 | return -EINVAL; | |
2499 | ||
2500 | x = ((uint16_t) aa << 12) | | |
2501 | ((uint16_t) bb << 8) | | |
2502 | ((uint16_t) cc << 4) | | |
2503 | ((uint16_t) dd); | |
2504 | ||
2505 | if (x <= 0) | |
2506 | return -EINVAL; | |
2507 | ||
2508 | *ret = x; | |
2509 | ||
2510 | return 0; | |
2511 | } | |
2512 | ||
2513 | static int json_parse_string(const char **p, char **ret) { | |
2514 | _cleanup_free_ char *s = NULL; | |
319a4f4b | 2515 | size_t n = 0; |
cd0b6c53 LP |
2516 | const char *c; |
2517 | ||
2518 | assert(p); | |
2519 | assert(*p); | |
2520 | assert(ret); | |
2521 | ||
2522 | c = *p; | |
2523 | ||
2524 | if (*c != '"') | |
2525 | return -EINVAL; | |
2526 | ||
2527 | c++; | |
2528 | ||
2529 | for (;;) { | |
2530 | int len; | |
2531 | ||
2532 | /* Check for EOF */ | |
2533 | if (*c == 0) | |
2534 | return -EINVAL; | |
2535 | ||
2536 | /* Check for control characters 0x00..0x1f */ | |
2537 | if (*c > 0 && *c < ' ') | |
2538 | return -EINVAL; | |
2539 | ||
2540 | /* Check for control character 0x7f */ | |
2541 | if (*c == 0x7f) | |
2542 | return -EINVAL; | |
2543 | ||
2544 | if (*c == '"') { | |
2545 | if (!s) { | |
2546 | s = strdup(""); | |
2547 | if (!s) | |
2548 | return -ENOMEM; | |
2549 | } else | |
2550 | s[n] = 0; | |
2551 | ||
2552 | *p = c + 1; | |
2553 | ||
490c5a37 | 2554 | *ret = TAKE_PTR(s); |
cd0b6c53 LP |
2555 | return JSON_TOKEN_STRING; |
2556 | } | |
2557 | ||
2558 | if (*c == '\\') { | |
2559 | char ch = 0; | |
2560 | c++; | |
2561 | ||
2562 | if (*c == 0) | |
2563 | return -EINVAL; | |
2564 | ||
2565 | if (IN_SET(*c, '"', '\\', '/')) | |
2566 | ch = *c; | |
2567 | else if (*c == 'b') | |
2568 | ch = '\b'; | |
2569 | else if (*c == 'f') | |
2570 | ch = '\f'; | |
2571 | else if (*c == 'n') | |
2572 | ch = '\n'; | |
2573 | else if (*c == 'r') | |
2574 | ch = '\r'; | |
2575 | else if (*c == 't') | |
2576 | ch = '\t'; | |
2577 | else if (*c == 'u') { | |
2578 | char16_t x; | |
2579 | int r; | |
2580 | ||
2581 | r = unhex_ucs2(c + 1, &x); | |
2582 | if (r < 0) | |
2583 | return r; | |
2584 | ||
2585 | c += 5; | |
2586 | ||
319a4f4b | 2587 | if (!GREEDY_REALLOC(s, n + 5)) |
cd0b6c53 LP |
2588 | return -ENOMEM; |
2589 | ||
2590 | if (!utf16_is_surrogate(x)) | |
2591 | n += utf8_encode_unichar(s + n, (char32_t) x); | |
2592 | else if (utf16_is_trailing_surrogate(x)) | |
2593 | return -EINVAL; | |
2594 | else { | |
2595 | char16_t y; | |
2596 | ||
2597 | if (c[0] != '\\' || c[1] != 'u') | |
2598 | return -EINVAL; | |
2599 | ||
2600 | r = unhex_ucs2(c + 2, &y); | |
2601 | if (r < 0) | |
2602 | return r; | |
2603 | ||
2604 | c += 6; | |
2605 | ||
2606 | if (!utf16_is_trailing_surrogate(y)) | |
2607 | return -EINVAL; | |
2608 | ||
2609 | n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y)); | |
2610 | } | |
2611 | ||
2612 | continue; | |
2613 | } else | |
2614 | return -EINVAL; | |
2615 | ||
319a4f4b | 2616 | if (!GREEDY_REALLOC(s, n + 2)) |
cd0b6c53 LP |
2617 | return -ENOMEM; |
2618 | ||
2619 | s[n++] = ch; | |
b3a9d980 | 2620 | c++; |
cd0b6c53 LP |
2621 | continue; |
2622 | } | |
2623 | ||
f5fbe71d | 2624 | len = utf8_encoded_valid_unichar(c, SIZE_MAX); |
cd0b6c53 LP |
2625 | if (len < 0) |
2626 | return len; | |
2627 | ||
319a4f4b | 2628 | if (!GREEDY_REALLOC(s, n + len + 1)) |
cd0b6c53 LP |
2629 | return -ENOMEM; |
2630 | ||
2631 | memcpy(s + n, c, len); | |
2632 | n += len; | |
2633 | c += len; | |
2634 | } | |
2635 | } | |
2636 | ||
2637 | static int json_parse_number(const char **p, JsonValue *ret) { | |
2638 | bool negative = false, exponent_negative = false, is_real = false; | |
337712e7 | 2639 | double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0; |
718ca772 ZJS |
2640 | int64_t i = 0; |
2641 | uint64_t u = 0; | |
cd0b6c53 LP |
2642 | const char *c; |
2643 | ||
2644 | assert(p); | |
2645 | assert(*p); | |
2646 | assert(ret); | |
2647 | ||
2648 | c = *p; | |
2649 | ||
2650 | if (*c == '-') { | |
2651 | negative = true; | |
2652 | c++; | |
2653 | } | |
2654 | ||
2655 | if (*c == '0') | |
2656 | c++; | |
2657 | else { | |
2658 | if (!strchr("123456789", *c) || *c == 0) | |
2659 | return -EINVAL; | |
2660 | ||
2661 | do { | |
2662 | if (!is_real) { | |
2663 | if (negative) { | |
2664 | ||
718ca772 | 2665 | if (i < INT64_MIN / 10) /* overflow */ |
cd0b6c53 LP |
2666 | is_real = true; |
2667 | else { | |
718ca772 | 2668 | int64_t t = 10 * i; |
cd0b6c53 | 2669 | |
718ca772 | 2670 | if (t < INT64_MIN + (*c - '0')) /* overflow */ |
cd0b6c53 LP |
2671 | is_real = true; |
2672 | else | |
2673 | i = t - (*c - '0'); | |
2674 | } | |
2675 | } else { | |
718ca772 | 2676 | if (u > UINT64_MAX / 10) /* overflow */ |
cd0b6c53 LP |
2677 | is_real = true; |
2678 | else { | |
718ca772 | 2679 | uint64_t t = 10 * u; |
cd0b6c53 | 2680 | |
718ca772 | 2681 | if (t > UINT64_MAX - (*c - '0')) /* overflow */ |
cd0b6c53 LP |
2682 | is_real = true; |
2683 | else | |
2684 | u = t + (*c - '0'); | |
2685 | } | |
2686 | } | |
2687 | } | |
2688 | ||
2689 | x = 10.0 * x + (*c - '0'); | |
2690 | ||
2691 | c++; | |
2692 | } while (strchr("0123456789", *c) && *c != 0); | |
2693 | } | |
2694 | ||
2695 | if (*c == '.') { | |
2696 | is_real = true; | |
2697 | c++; | |
2698 | ||
2699 | if (!strchr("0123456789", *c) || *c == 0) | |
2700 | return -EINVAL; | |
2701 | ||
2702 | do { | |
2703 | y = 10.0 * y + (*c - '0'); | |
2704 | shift = 10.0 * shift; | |
2705 | c++; | |
2706 | } while (strchr("0123456789", *c) && *c != 0); | |
2707 | } | |
2708 | ||
490c5a37 | 2709 | if (IN_SET(*c, 'e', 'E')) { |
cd0b6c53 LP |
2710 | is_real = true; |
2711 | c++; | |
2712 | ||
2713 | if (*c == '-') { | |
2714 | exponent_negative = true; | |
2715 | c++; | |
2716 | } else if (*c == '+') | |
2717 | c++; | |
2718 | ||
2719 | if (!strchr("0123456789", *c) || *c == 0) | |
2720 | return -EINVAL; | |
2721 | ||
2722 | do { | |
2723 | exponent = 10.0 * exponent + (*c - '0'); | |
2724 | c++; | |
2725 | } while (strchr("0123456789", *c) && *c != 0); | |
2726 | } | |
2727 | ||
2728 | *p = c; | |
2729 | ||
2730 | if (is_real) { | |
337712e7 | 2731 | ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent); |
cd0b6c53 LP |
2732 | return JSON_TOKEN_REAL; |
2733 | } else if (negative) { | |
2734 | ret->integer = i; | |
2735 | return JSON_TOKEN_INTEGER; | |
2736 | } else { | |
2737 | ret->unsig = u; | |
2738 | return JSON_TOKEN_UNSIGNED; | |
2739 | } | |
2740 | } | |
2741 | ||
2742 | int json_tokenize( | |
2743 | const char **p, | |
2744 | char **ret_string, | |
2745 | JsonValue *ret_value, | |
2746 | unsigned *ret_line, /* 'ret_line' returns the line at the beginning of this token */ | |
2747 | unsigned *ret_column, | |
2748 | void **state, | |
2749 | unsigned *line, /* 'line' is used as a line state, it always reflect the line we are at after the token was read */ | |
2750 | unsigned *column) { | |
2751 | ||
2752 | unsigned start_line, start_column; | |
2753 | const char *start, *c; | |
2754 | size_t n; | |
2755 | int t, r; | |
2756 | ||
2757 | enum { | |
2758 | STATE_NULL, | |
2759 | STATE_VALUE, | |
2760 | STATE_VALUE_POST, | |
2761 | }; | |
2762 | ||
2763 | assert(p); | |
2764 | assert(*p); | |
2765 | assert(ret_string); | |
2766 | assert(ret_value); | |
2767 | assert(ret_line); | |
2768 | assert(ret_column); | |
2769 | assert(line); | |
2770 | assert(column); | |
2771 | assert(state); | |
2772 | ||
2773 | t = PTR_TO_INT(*state); | |
2774 | if (t == STATE_NULL) { | |
2775 | *line = 1; | |
2776 | *column = 1; | |
2777 | t = STATE_VALUE; | |
2778 | } | |
2779 | ||
2780 | /* Skip over the whitespace */ | |
2781 | n = strspn(*p, WHITESPACE); | |
2782 | inc_lines_columns(line, column, *p, n); | |
2783 | c = *p + n; | |
2784 | ||
2785 | /* Remember where we started processing this token */ | |
2786 | start = c; | |
2787 | start_line = *line; | |
2788 | start_column = *column; | |
2789 | ||
2790 | if (*c == 0) { | |
2791 | *ret_string = NULL; | |
2792 | *ret_value = JSON_VALUE_NULL; | |
2793 | r = JSON_TOKEN_END; | |
2794 | goto finish; | |
2795 | } | |
2796 | ||
2797 | switch (t) { | |
2798 | ||
2799 | case STATE_VALUE: | |
2800 | ||
2801 | if (*c == '{') { | |
2802 | c++; | |
2803 | *state = INT_TO_PTR(STATE_VALUE); | |
2804 | r = JSON_TOKEN_OBJECT_OPEN; | |
2805 | goto null_return; | |
2806 | ||
2807 | } else if (*c == '}') { | |
2808 | c++; | |
2809 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2810 | r = JSON_TOKEN_OBJECT_CLOSE; | |
2811 | goto null_return; | |
2812 | ||
2813 | } else if (*c == '[') { | |
2814 | c++; | |
2815 | *state = INT_TO_PTR(STATE_VALUE); | |
2816 | r = JSON_TOKEN_ARRAY_OPEN; | |
2817 | goto null_return; | |
2818 | ||
2819 | } else if (*c == ']') { | |
2820 | c++; | |
2821 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2822 | r = JSON_TOKEN_ARRAY_CLOSE; | |
2823 | goto null_return; | |
2824 | ||
2825 | } else if (*c == '"') { | |
2826 | ||
2827 | r = json_parse_string(&c, ret_string); | |
2828 | if (r < 0) | |
2829 | return r; | |
2830 | ||
2831 | *ret_value = JSON_VALUE_NULL; | |
2832 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2833 | goto finish; | |
2834 | ||
2835 | } else if (strchr("-0123456789", *c)) { | |
2836 | ||
2837 | r = json_parse_number(&c, ret_value); | |
2838 | if (r < 0) | |
2839 | return r; | |
2840 | ||
2841 | *ret_string = NULL; | |
2842 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2843 | goto finish; | |
2844 | ||
2845 | } else if (startswith(c, "true")) { | |
2846 | *ret_string = NULL; | |
2847 | ret_value->boolean = true; | |
2848 | c += 4; | |
2849 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2850 | r = JSON_TOKEN_BOOLEAN; | |
2851 | goto finish; | |
2852 | ||
2853 | } else if (startswith(c, "false")) { | |
2854 | *ret_string = NULL; | |
2855 | ret_value->boolean = false; | |
2856 | c += 5; | |
2857 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2858 | r = JSON_TOKEN_BOOLEAN; | |
2859 | goto finish; | |
2860 | ||
2861 | } else if (startswith(c, "null")) { | |
2862 | *ret_string = NULL; | |
2863 | *ret_value = JSON_VALUE_NULL; | |
2864 | c += 4; | |
2865 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2866 | r = JSON_TOKEN_NULL; | |
2867 | goto finish; | |
2868 | ||
2869 | } | |
2870 | ||
2871 | return -EINVAL; | |
2872 | ||
2873 | case STATE_VALUE_POST: | |
2874 | ||
2875 | if (*c == ':') { | |
2876 | c++; | |
2877 | *state = INT_TO_PTR(STATE_VALUE); | |
2878 | r = JSON_TOKEN_COLON; | |
2879 | goto null_return; | |
2880 | ||
2881 | } else if (*c == ',') { | |
2882 | c++; | |
2883 | *state = INT_TO_PTR(STATE_VALUE); | |
2884 | r = JSON_TOKEN_COMMA; | |
2885 | goto null_return; | |
2886 | ||
2887 | } else if (*c == '}') { | |
2888 | c++; | |
2889 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2890 | r = JSON_TOKEN_OBJECT_CLOSE; | |
2891 | goto null_return; | |
2892 | ||
2893 | } else if (*c == ']') { | |
2894 | c++; | |
2895 | *state = INT_TO_PTR(STATE_VALUE_POST); | |
2896 | r = JSON_TOKEN_ARRAY_CLOSE; | |
2897 | goto null_return; | |
2898 | } | |
2899 | ||
2900 | return -EINVAL; | |
2901 | ||
2902 | default: | |
04499a70 | 2903 | assert_not_reached(); |
cd0b6c53 LP |
2904 | } |
2905 | ||
2906 | null_return: | |
2907 | *ret_string = NULL; | |
2908 | *ret_value = JSON_VALUE_NULL; | |
2909 | ||
2910 | finish: | |
2911 | inc_lines_columns(line, column, start, c - start); | |
2912 | *p = c; | |
2913 | ||
2914 | *ret_line = start_line; | |
2915 | *ret_column = start_column; | |
2916 | ||
2917 | return r; | |
2918 | } | |
2919 | ||
2920 | typedef enum JsonExpect { | |
2921 | /* The following values are used by json_parse() */ | |
2922 | EXPECT_TOPLEVEL, | |
2923 | EXPECT_END, | |
2924 | EXPECT_OBJECT_FIRST_KEY, | |
2925 | EXPECT_OBJECT_NEXT_KEY, | |
2926 | EXPECT_OBJECT_COLON, | |
2927 | EXPECT_OBJECT_VALUE, | |
2928 | EXPECT_OBJECT_COMMA, | |
2929 | EXPECT_ARRAY_FIRST_ELEMENT, | |
2930 | EXPECT_ARRAY_NEXT_ELEMENT, | |
2931 | EXPECT_ARRAY_COMMA, | |
2932 | ||
2933 | /* And these are used by json_build() */ | |
2934 | EXPECT_ARRAY_ELEMENT, | |
2935 | EXPECT_OBJECT_KEY, | |
2936 | } JsonExpect; | |
2937 | ||
2938 | typedef struct JsonStack { | |
2939 | JsonExpect expect; | |
2940 | JsonVariant **elements; | |
319a4f4b | 2941 | size_t n_elements; |
cd0b6c53 LP |
2942 | unsigned line_before; |
2943 | unsigned column_before; | |
f5fbe71d | 2944 | size_t n_suppress; /* When building: if > 0, suppress this many subsequent elements. If == SIZE_MAX, suppress all subsequent elements */ |
cd0b6c53 LP |
2945 | } JsonStack; |
2946 | ||
2947 | static void json_stack_release(JsonStack *s) { | |
2948 | assert(s); | |
2949 | ||
ee9d31a6 | 2950 | CLEANUP_ARRAY(s->elements, s->n_elements, json_variant_unref_many); |
cd0b6c53 LP |
2951 | } |
2952 | ||
2953 | static int json_parse_internal( | |
2954 | const char **input, | |
2955 | JsonSource *source, | |
d642f640 | 2956 | JsonParseFlags flags, |
cd0b6c53 LP |
2957 | JsonVariant **ret, |
2958 | unsigned *line, | |
2959 | unsigned *column, | |
2960 | bool continue_end) { | |
2961 | ||
a4669764 | 2962 | size_t n_stack = 1; |
cd0b6c53 LP |
2963 | unsigned line_buffer = 0, column_buffer = 0; |
2964 | void *tokenizer_state = NULL; | |
2965 | JsonStack *stack = NULL; | |
2966 | const char *p; | |
2967 | int r; | |
2968 | ||
2969 | assert_return(input, -EINVAL); | |
2970 | assert_return(ret, -EINVAL); | |
2971 | ||
2972 | p = *input; | |
2973 | ||
319a4f4b | 2974 | if (!GREEDY_REALLOC(stack, n_stack)) |
cd0b6c53 LP |
2975 | return -ENOMEM; |
2976 | ||
2977 | stack[0] = (JsonStack) { | |
2978 | .expect = EXPECT_TOPLEVEL, | |
2979 | }; | |
2980 | ||
2981 | if (!line) | |
2982 | line = &line_buffer; | |
2983 | if (!column) | |
2984 | column = &column_buffer; | |
2985 | ||
2986 | for (;;) { | |
6980b04f | 2987 | _cleanup_(json_variant_unrefp) JsonVariant *add = NULL; |
cd0b6c53 LP |
2988 | _cleanup_free_ char *string = NULL; |
2989 | unsigned line_token, column_token; | |
cd0b6c53 LP |
2990 | JsonStack *current; |
2991 | JsonValue value; | |
2992 | int token; | |
2993 | ||
2994 | assert(n_stack > 0); | |
2995 | current = stack + n_stack - 1; | |
2996 | ||
2997 | if (continue_end && current->expect == EXPECT_END) | |
2998 | goto done; | |
2999 | ||
3000 | token = json_tokenize(&p, &string, &value, &line_token, &column_token, &tokenizer_state, line, column); | |
3001 | if (token < 0) { | |
3002 | r = token; | |
3003 | goto finish; | |
3004 | } | |
3005 | ||
3006 | switch (token) { | |
3007 | ||
3008 | case JSON_TOKEN_END: | |
3009 | if (current->expect != EXPECT_END) { | |
3010 | r = -EINVAL; | |
3011 | goto finish; | |
3012 | } | |
3013 | ||
3014 | assert(current->n_elements == 1); | |
3015 | assert(n_stack == 1); | |
3016 | goto done; | |
3017 | ||
3018 | case JSON_TOKEN_COLON: | |
3019 | ||
3020 | if (current->expect != EXPECT_OBJECT_COLON) { | |
3021 | r = -EINVAL; | |
3022 | goto finish; | |
3023 | } | |
3024 | ||
3025 | current->expect = EXPECT_OBJECT_VALUE; | |
3026 | break; | |
3027 | ||
3028 | case JSON_TOKEN_COMMA: | |
3029 | ||
3030 | if (current->expect == EXPECT_OBJECT_COMMA) | |
3031 | current->expect = EXPECT_OBJECT_NEXT_KEY; | |
3032 | else if (current->expect == EXPECT_ARRAY_COMMA) | |
3033 | current->expect = EXPECT_ARRAY_NEXT_ELEMENT; | |
3034 | else { | |
3035 | r = -EINVAL; | |
3036 | goto finish; | |
3037 | } | |
3038 | ||
3039 | break; | |
3040 | ||
3041 | case JSON_TOKEN_OBJECT_OPEN: | |
3042 | ||
3043 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3044 | r = -EINVAL; | |
3045 | goto finish; | |
3046 | } | |
3047 | ||
319a4f4b | 3048 | if (!GREEDY_REALLOC(stack, n_stack+1)) { |
cd0b6c53 LP |
3049 | r = -ENOMEM; |
3050 | goto finish; | |
3051 | } | |
3052 | current = stack + n_stack - 1; | |
3053 | ||
3054 | /* Prepare the expect for when we return from the child */ | |
3055 | if (current->expect == EXPECT_TOPLEVEL) | |
3056 | current->expect = EXPECT_END; | |
3057 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3058 | current->expect = EXPECT_OBJECT_COMMA; | |
3059 | else { | |
3060 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3061 | current->expect = EXPECT_ARRAY_COMMA; | |
3062 | } | |
3063 | ||
3064 | stack[n_stack++] = (JsonStack) { | |
3065 | .expect = EXPECT_OBJECT_FIRST_KEY, | |
3066 | .line_before = line_token, | |
3067 | .column_before = column_token, | |
3068 | }; | |
3069 | ||
3070 | current = stack + n_stack - 1; | |
3071 | break; | |
3072 | ||
3073 | case JSON_TOKEN_OBJECT_CLOSE: | |
3074 | if (!IN_SET(current->expect, EXPECT_OBJECT_FIRST_KEY, EXPECT_OBJECT_COMMA)) { | |
3075 | r = -EINVAL; | |
3076 | goto finish; | |
3077 | } | |
3078 | ||
3079 | assert(n_stack > 1); | |
3080 | ||
3081 | r = json_variant_new_object(&add, current->elements, current->n_elements); | |
3082 | if (r < 0) | |
3083 | goto finish; | |
3084 | ||
3085 | line_token = current->line_before; | |
3086 | column_token = current->column_before; | |
3087 | ||
3088 | json_stack_release(current); | |
3089 | n_stack--, current--; | |
3090 | ||
3091 | break; | |
3092 | ||
3093 | case JSON_TOKEN_ARRAY_OPEN: | |
3094 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3095 | r = -EINVAL; | |
3096 | goto finish; | |
3097 | } | |
3098 | ||
319a4f4b | 3099 | if (!GREEDY_REALLOC(stack, n_stack+1)) { |
cd0b6c53 LP |
3100 | r = -ENOMEM; |
3101 | goto finish; | |
3102 | } | |
3103 | current = stack + n_stack - 1; | |
3104 | ||
3105 | /* Prepare the expect for when we return from the child */ | |
3106 | if (current->expect == EXPECT_TOPLEVEL) | |
3107 | current->expect = EXPECT_END; | |
3108 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3109 | current->expect = EXPECT_OBJECT_COMMA; | |
3110 | else { | |
3111 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3112 | current->expect = EXPECT_ARRAY_COMMA; | |
3113 | } | |
3114 | ||
3115 | stack[n_stack++] = (JsonStack) { | |
3116 | .expect = EXPECT_ARRAY_FIRST_ELEMENT, | |
3117 | .line_before = line_token, | |
3118 | .column_before = column_token, | |
3119 | }; | |
3120 | ||
3121 | break; | |
3122 | ||
3123 | case JSON_TOKEN_ARRAY_CLOSE: | |
3124 | if (!IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_COMMA)) { | |
3125 | r = -EINVAL; | |
3126 | goto finish; | |
3127 | } | |
3128 | ||
3129 | assert(n_stack > 1); | |
3130 | ||
3131 | r = json_variant_new_array(&add, current->elements, current->n_elements); | |
3132 | if (r < 0) | |
3133 | goto finish; | |
3134 | ||
3135 | line_token = current->line_before; | |
3136 | column_token = current->column_before; | |
3137 | ||
3138 | json_stack_release(current); | |
3139 | n_stack--, current--; | |
3140 | break; | |
3141 | ||
3142 | case JSON_TOKEN_STRING: | |
3143 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_FIRST_KEY, EXPECT_OBJECT_NEXT_KEY, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3144 | r = -EINVAL; | |
3145 | goto finish; | |
3146 | } | |
3147 | ||
3148 | r = json_variant_new_string(&add, string); | |
3149 | if (r < 0) | |
3150 | goto finish; | |
3151 | ||
3152 | if (current->expect == EXPECT_TOPLEVEL) | |
3153 | current->expect = EXPECT_END; | |
3154 | else if (IN_SET(current->expect, EXPECT_OBJECT_FIRST_KEY, EXPECT_OBJECT_NEXT_KEY)) | |
3155 | current->expect = EXPECT_OBJECT_COLON; | |
3156 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3157 | current->expect = EXPECT_OBJECT_COMMA; | |
3158 | else { | |
3159 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3160 | current->expect = EXPECT_ARRAY_COMMA; | |
3161 | } | |
3162 | ||
3163 | break; | |
3164 | ||
3165 | case JSON_TOKEN_REAL: | |
3166 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3167 | r = -EINVAL; | |
3168 | goto finish; | |
3169 | } | |
3170 | ||
3171 | r = json_variant_new_real(&add, value.real); | |
3172 | if (r < 0) | |
3173 | goto finish; | |
3174 | ||
3175 | if (current->expect == EXPECT_TOPLEVEL) | |
3176 | current->expect = EXPECT_END; | |
3177 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3178 | current->expect = EXPECT_OBJECT_COMMA; | |
3179 | else { | |
3180 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3181 | current->expect = EXPECT_ARRAY_COMMA; | |
3182 | } | |
3183 | ||
3184 | break; | |
3185 | ||
3186 | case JSON_TOKEN_INTEGER: | |
3187 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3188 | r = -EINVAL; | |
3189 | goto finish; | |
3190 | } | |
3191 | ||
3192 | r = json_variant_new_integer(&add, value.integer); | |
3193 | if (r < 0) | |
3194 | goto finish; | |
3195 | ||
3196 | if (current->expect == EXPECT_TOPLEVEL) | |
3197 | current->expect = EXPECT_END; | |
3198 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3199 | current->expect = EXPECT_OBJECT_COMMA; | |
3200 | else { | |
3201 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3202 | current->expect = EXPECT_ARRAY_COMMA; | |
3203 | } | |
3204 | ||
3205 | break; | |
3206 | ||
3207 | case JSON_TOKEN_UNSIGNED: | |
3208 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3209 | r = -EINVAL; | |
3210 | goto finish; | |
3211 | } | |
3212 | ||
3213 | r = json_variant_new_unsigned(&add, value.unsig); | |
3214 | if (r < 0) | |
3215 | goto finish; | |
3216 | ||
3217 | if (current->expect == EXPECT_TOPLEVEL) | |
3218 | current->expect = EXPECT_END; | |
3219 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3220 | current->expect = EXPECT_OBJECT_COMMA; | |
3221 | else { | |
3222 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3223 | current->expect = EXPECT_ARRAY_COMMA; | |
3224 | } | |
3225 | ||
3226 | break; | |
3227 | ||
3228 | case JSON_TOKEN_BOOLEAN: | |
3229 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3230 | r = -EINVAL; | |
3231 | goto finish; | |
3232 | } | |
3233 | ||
3234 | r = json_variant_new_boolean(&add, value.boolean); | |
3235 | if (r < 0) | |
3236 | goto finish; | |
3237 | ||
3238 | if (current->expect == EXPECT_TOPLEVEL) | |
3239 | current->expect = EXPECT_END; | |
3240 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3241 | current->expect = EXPECT_OBJECT_COMMA; | |
3242 | else { | |
3243 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3244 | current->expect = EXPECT_ARRAY_COMMA; | |
3245 | } | |
3246 | ||
3247 | break; | |
3248 | ||
3249 | case JSON_TOKEN_NULL: | |
3250 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) { | |
3251 | r = -EINVAL; | |
3252 | goto finish; | |
3253 | } | |
3254 | ||
3255 | r = json_variant_new_null(&add); | |
3256 | if (r < 0) | |
3257 | goto finish; | |
3258 | ||
3259 | if (current->expect == EXPECT_TOPLEVEL) | |
3260 | current->expect = EXPECT_END; | |
3261 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3262 | current->expect = EXPECT_OBJECT_COMMA; | |
3263 | else { | |
3264 | assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)); | |
3265 | current->expect = EXPECT_ARRAY_COMMA; | |
3266 | } | |
3267 | ||
3268 | break; | |
3269 | ||
3270 | default: | |
04499a70 | 3271 | assert_not_reached(); |
cd0b6c53 LP |
3272 | } |
3273 | ||
3274 | if (add) { | |
d642f640 LP |
3275 | /* If we are asked to make this parsed object sensitive, then let's apply this |
3276 | * immediately after allocating each variant, so that when we abort half-way | |
3277 | * everything we already allocated that is then freed is correctly marked. */ | |
3278 | if (FLAGS_SET(flags, JSON_PARSE_SENSITIVE)) | |
3279 | json_variant_sensitive(add); | |
3280 | ||
cd0b6c53 LP |
3281 | (void) json_variant_set_source(&add, source, line_token, column_token); |
3282 | ||
319a4f4b | 3283 | if (!GREEDY_REALLOC(current->elements, current->n_elements + 1)) { |
cd0b6c53 LP |
3284 | r = -ENOMEM; |
3285 | goto finish; | |
3286 | } | |
3287 | ||
6980b04f | 3288 | current->elements[current->n_elements++] = TAKE_PTR(add); |
cd0b6c53 LP |
3289 | } |
3290 | } | |
3291 | ||
3292 | done: | |
3293 | assert(n_stack == 1); | |
3294 | assert(stack[0].n_elements == 1); | |
3295 | ||
3296 | *ret = json_variant_ref(stack[0].elements[0]); | |
3297 | *input = p; | |
3298 | r = 0; | |
3299 | ||
3300 | finish: | |
a4669764 | 3301 | for (size_t i = 0; i < n_stack; i++) |
cd0b6c53 LP |
3302 | json_stack_release(stack + i); |
3303 | ||
3304 | free(stack); | |
3305 | ||
3306 | return r; | |
3307 | } | |
3308 | ||
b0eeb945 ZJS |
3309 | int json_parse_with_source( |
3310 | const char *input, | |
3311 | const char *source, | |
3312 | JsonParseFlags flags, | |
3313 | JsonVariant **ret, | |
3314 | unsigned *ret_line, | |
3315 | unsigned *ret_column) { | |
3316 | ||
3317 | _cleanup_(json_source_unrefp) JsonSource *s = NULL; | |
3318 | ||
3319 | if (source) { | |
3320 | s = json_source_new(source); | |
3321 | if (!s) | |
3322 | return -ENOMEM; | |
3323 | } | |
3324 | ||
3325 | return json_parse_internal(&input, s, flags, ret, ret_line, ret_column, false); | |
cd0b6c53 LP |
3326 | } |
3327 | ||
b0eeb945 ZJS |
3328 | int json_parse_with_source_continue( |
3329 | const char **p, | |
3330 | const char *source, | |
3331 | JsonParseFlags flags, | |
3332 | JsonVariant **ret, | |
3333 | unsigned *ret_line, | |
3334 | unsigned *ret_column) { | |
3335 | ||
3336 | _cleanup_(json_source_unrefp) JsonSource *s = NULL; | |
3337 | ||
3338 | if (source) { | |
3339 | s = json_source_new(source); | |
3340 | if (!s) | |
3341 | return -ENOMEM; | |
3342 | } | |
3343 | ||
3344 | return json_parse_internal(p, s, flags, ret, ret_line, ret_column, true); | |
cd0b6c53 LP |
3345 | } |
3346 | ||
b0eeb945 ZJS |
3347 | int json_parse_file_at( |
3348 | FILE *f, | |
3349 | int dir_fd, | |
3350 | const char *path, | |
3351 | JsonParseFlags flags, | |
3352 | JsonVariant **ret, | |
3353 | unsigned *ret_line, | |
3354 | unsigned *ret_column) { | |
3355 | ||
cd0b6c53 | 3356 | _cleanup_free_ char *text = NULL; |
cd0b6c53 LP |
3357 | int r; |
3358 | ||
3359 | if (f) | |
3360 | r = read_full_stream(f, &text, NULL); | |
3361 | else if (path) | |
986311c2 | 3362 | r = read_full_file_full(dir_fd, path, UINT64_MAX, SIZE_MAX, 0, NULL, &text, NULL); |
cd0b6c53 LP |
3363 | else |
3364 | return -EINVAL; | |
3365 | if (r < 0) | |
3366 | return r; | |
3367 | ||
87a16eb8 ZJS |
3368 | if (isempty(text)) |
3369 | return -ENODATA; | |
3370 | ||
b0eeb945 | 3371 | return json_parse_with_source(text, path, flags, ret, ret_line, ret_column); |
cd0b6c53 LP |
3372 | } |
3373 | ||
3374 | int json_buildv(JsonVariant **ret, va_list ap) { | |
3375 | JsonStack *stack = NULL; | |
a4669764 | 3376 | size_t n_stack = 1; |
8eb735b8 | 3377 | const char *name = NULL; |
cd0b6c53 LP |
3378 | int r; |
3379 | ||
3380 | assert_return(ret, -EINVAL); | |
3381 | ||
319a4f4b | 3382 | if (!GREEDY_REALLOC(stack, n_stack)) |
cd0b6c53 LP |
3383 | return -ENOMEM; |
3384 | ||
3385 | stack[0] = (JsonStack) { | |
3386 | .expect = EXPECT_TOPLEVEL, | |
3387 | }; | |
3388 | ||
3389 | for (;;) { | |
7314084a | 3390 | _cleanup_(json_variant_unrefp) JsonVariant *add = NULL, *add_more = NULL; |
319a4f27 LP |
3391 | size_t n_subtract = 0; /* how much to subtract from current->n_suppress, i.e. how many elements would |
3392 | * have been added to the current variant */ | |
cd0b6c53 LP |
3393 | JsonStack *current; |
3394 | int command; | |
3395 | ||
3396 | assert(n_stack > 0); | |
3397 | current = stack + n_stack - 1; | |
3398 | ||
3399 | if (current->expect == EXPECT_END) | |
3400 | goto done; | |
3401 | ||
3402 | command = va_arg(ap, int); | |
3403 | ||
3404 | switch (command) { | |
3405 | ||
3406 | case _JSON_BUILD_STRING: { | |
3407 | const char *p; | |
3408 | ||
3409 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3410 | r = -EINVAL; | |
3411 | goto finish; | |
3412 | } | |
3413 | ||
3414 | p = va_arg(ap, const char *); | |
3415 | ||
319a4f27 LP |
3416 | if (current->n_suppress == 0) { |
3417 | r = json_variant_new_string(&add, p); | |
3418 | if (r < 0) | |
3419 | goto finish; | |
3420 | } | |
3421 | ||
3422 | n_subtract = 1; | |
cd0b6c53 LP |
3423 | |
3424 | if (current->expect == EXPECT_TOPLEVEL) | |
3425 | current->expect = EXPECT_END; | |
3426 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3427 | current->expect = EXPECT_OBJECT_KEY; | |
3428 | else | |
3429 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3430 | ||
3431 | break; | |
3432 | } | |
3433 | ||
3434 | case _JSON_BUILD_INTEGER: { | |
718ca772 | 3435 | int64_t j; |
cd0b6c53 LP |
3436 | |
3437 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3438 | r = -EINVAL; | |
3439 | goto finish; | |
3440 | } | |
3441 | ||
718ca772 | 3442 | j = va_arg(ap, int64_t); |
cd0b6c53 | 3443 | |
319a4f27 LP |
3444 | if (current->n_suppress == 0) { |
3445 | r = json_variant_new_integer(&add, j); | |
3446 | if (r < 0) | |
3447 | goto finish; | |
3448 | } | |
3449 | ||
3450 | n_subtract = 1; | |
cd0b6c53 LP |
3451 | |
3452 | if (current->expect == EXPECT_TOPLEVEL) | |
3453 | current->expect = EXPECT_END; | |
3454 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3455 | current->expect = EXPECT_OBJECT_KEY; | |
3456 | else | |
3457 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3458 | ||
3459 | break; | |
3460 | } | |
3461 | ||
3462 | case _JSON_BUILD_UNSIGNED: { | |
718ca772 | 3463 | uint64_t j; |
cd0b6c53 LP |
3464 | |
3465 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3466 | r = -EINVAL; | |
3467 | goto finish; | |
3468 | } | |
3469 | ||
718ca772 | 3470 | j = va_arg(ap, uint64_t); |
cd0b6c53 | 3471 | |
319a4f27 LP |
3472 | if (current->n_suppress == 0) { |
3473 | r = json_variant_new_unsigned(&add, j); | |
3474 | if (r < 0) | |
3475 | goto finish; | |
3476 | } | |
3477 | ||
3478 | n_subtract = 1; | |
cd0b6c53 LP |
3479 | |
3480 | if (current->expect == EXPECT_TOPLEVEL) | |
3481 | current->expect = EXPECT_END; | |
3482 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3483 | current->expect = EXPECT_OBJECT_KEY; | |
3484 | else | |
3485 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3486 | ||
3487 | break; | |
3488 | } | |
3489 | ||
3490 | case _JSON_BUILD_REAL: { | |
337712e7 | 3491 | double d; |
cd0b6c53 LP |
3492 | |
3493 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3494 | r = -EINVAL; | |
3495 | goto finish; | |
3496 | } | |
3497 | ||
337712e7 | 3498 | d = va_arg(ap, double); |
cd0b6c53 | 3499 | |
319a4f27 LP |
3500 | if (current->n_suppress == 0) { |
3501 | r = json_variant_new_real(&add, d); | |
3502 | if (r < 0) | |
3503 | goto finish; | |
3504 | } | |
3505 | ||
3506 | n_subtract = 1; | |
cd0b6c53 LP |
3507 | |
3508 | if (current->expect == EXPECT_TOPLEVEL) | |
3509 | current->expect = EXPECT_END; | |
3510 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3511 | current->expect = EXPECT_OBJECT_KEY; | |
3512 | else | |
3513 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3514 | ||
3515 | break; | |
3516 | } | |
3517 | ||
3518 | case _JSON_BUILD_BOOLEAN: { | |
3519 | bool b; | |
3520 | ||
3521 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3522 | r = -EINVAL; | |
3523 | goto finish; | |
3524 | } | |
3525 | ||
3526 | b = va_arg(ap, int); | |
3527 | ||
319a4f27 LP |
3528 | if (current->n_suppress == 0) { |
3529 | r = json_variant_new_boolean(&add, b); | |
3530 | if (r < 0) | |
3531 | goto finish; | |
3532 | } | |
3533 | ||
3534 | n_subtract = 1; | |
cd0b6c53 LP |
3535 | |
3536 | if (current->expect == EXPECT_TOPLEVEL) | |
3537 | current->expect = EXPECT_END; | |
3538 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3539 | current->expect = EXPECT_OBJECT_KEY; | |
3540 | else | |
3541 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3542 | ||
3543 | break; | |
3544 | } | |
3545 | ||
3546 | case _JSON_BUILD_NULL: | |
3547 | ||
3548 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3549 | r = -EINVAL; | |
3550 | goto finish; | |
3551 | } | |
3552 | ||
319a4f27 LP |
3553 | if (current->n_suppress == 0) { |
3554 | r = json_variant_new_null(&add); | |
3555 | if (r < 0) | |
3556 | goto finish; | |
3557 | } | |
3558 | ||
3559 | n_subtract = 1; | |
cd0b6c53 LP |
3560 | |
3561 | if (current->expect == EXPECT_TOPLEVEL) | |
3562 | current->expect = EXPECT_END; | |
3563 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3564 | current->expect = EXPECT_OBJECT_KEY; | |
3565 | else | |
3566 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3567 | ||
3568 | break; | |
3569 | ||
3570 | case _JSON_BUILD_VARIANT: | |
3571 | ||
3572 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3573 | r = -EINVAL; | |
3574 | goto finish; | |
3575 | } | |
3576 | ||
319a4f27 LP |
3577 | /* Note that we don't care for current->n_suppress here, after all the variant is already |
3578 | * allocated anyway... */ | |
cd0b6c53 LP |
3579 | add = va_arg(ap, JsonVariant*); |
3580 | if (!add) | |
3581 | add = JSON_VARIANT_MAGIC_NULL; | |
3582 | else | |
3583 | json_variant_ref(add); | |
3584 | ||
319a4f27 LP |
3585 | n_subtract = 1; |
3586 | ||
cd0b6c53 LP |
3587 | if (current->expect == EXPECT_TOPLEVEL) |
3588 | current->expect = EXPECT_END; | |
3589 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3590 | current->expect = EXPECT_OBJECT_KEY; | |
3591 | else | |
3592 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3593 | ||
3594 | break; | |
3595 | ||
e4defdc4 LP |
3596 | case _JSON_BUILD_VARIANT_ARRAY: { |
3597 | JsonVariant **array; | |
3598 | size_t n; | |
3599 | ||
3600 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3601 | r = -EINVAL; | |
3602 | goto finish; | |
3603 | } | |
3604 | ||
3605 | array = va_arg(ap, JsonVariant**); | |
3606 | n = va_arg(ap, size_t); | |
3607 | ||
3608 | if (current->n_suppress == 0) { | |
3609 | r = json_variant_new_array(&add, array, n); | |
3610 | if (r < 0) | |
3611 | goto finish; | |
3612 | } | |
3613 | ||
3614 | n_subtract = 1; | |
3615 | ||
3616 | if (current->expect == EXPECT_TOPLEVEL) | |
3617 | current->expect = EXPECT_END; | |
3618 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3619 | current->expect = EXPECT_OBJECT_KEY; | |
3620 | else | |
3621 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3622 | ||
3623 | break; | |
3624 | } | |
3625 | ||
cd0b6c53 LP |
3626 | case _JSON_BUILD_LITERAL: { |
3627 | const char *l; | |
3628 | ||
3629 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3630 | r = -EINVAL; | |
3631 | goto finish; | |
3632 | } | |
3633 | ||
3634 | l = va_arg(ap, const char *); | |
3635 | ||
319a4f27 LP |
3636 | if (l) { |
3637 | /* Note that we don't care for current->n_suppress here, we should generate parsing | |
3638 | * errors even in suppressed object properties */ | |
3639 | ||
d642f640 | 3640 | r = json_parse(l, 0, &add, NULL, NULL); |
cd0b6c53 LP |
3641 | if (r < 0) |
3642 | goto finish; | |
319a4f27 LP |
3643 | } else |
3644 | add = JSON_VARIANT_MAGIC_NULL; | |
3645 | ||
3646 | n_subtract = 1; | |
cd0b6c53 LP |
3647 | |
3648 | if (current->expect == EXPECT_TOPLEVEL) | |
3649 | current->expect = EXPECT_END; | |
3650 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3651 | current->expect = EXPECT_OBJECT_KEY; | |
3652 | else | |
3653 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3654 | ||
3655 | break; | |
3656 | } | |
3657 | ||
3658 | case _JSON_BUILD_ARRAY_BEGIN: | |
3659 | ||
3660 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3661 | r = -EINVAL; | |
3662 | goto finish; | |
3663 | } | |
3664 | ||
319a4f4b | 3665 | if (!GREEDY_REALLOC(stack, n_stack+1)) { |
cd0b6c53 LP |
3666 | r = -ENOMEM; |
3667 | goto finish; | |
3668 | } | |
3669 | current = stack + n_stack - 1; | |
3670 | ||
3671 | if (current->expect == EXPECT_TOPLEVEL) | |
3672 | current->expect = EXPECT_END; | |
3673 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3674 | current->expect = EXPECT_OBJECT_KEY; | |
3675 | else | |
3676 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3677 | ||
3678 | stack[n_stack++] = (JsonStack) { | |
3679 | .expect = EXPECT_ARRAY_ELEMENT, | |
f5fbe71d | 3680 | .n_suppress = current->n_suppress != 0 ? SIZE_MAX : 0, /* if we shall suppress the |
319a4f27 LP |
3681 | * new array, then we should |
3682 | * also suppress all array | |
3683 | * members */ | |
cd0b6c53 LP |
3684 | }; |
3685 | ||
3686 | break; | |
3687 | ||
3688 | case _JSON_BUILD_ARRAY_END: | |
3689 | if (current->expect != EXPECT_ARRAY_ELEMENT) { | |
3690 | r = -EINVAL; | |
3691 | goto finish; | |
3692 | } | |
3693 | ||
3694 | assert(n_stack > 1); | |
3695 | ||
319a4f27 LP |
3696 | if (current->n_suppress == 0) { |
3697 | r = json_variant_new_array(&add, current->elements, current->n_elements); | |
3698 | if (r < 0) | |
3699 | goto finish; | |
3700 | } | |
3701 | ||
3702 | n_subtract = 1; | |
cd0b6c53 LP |
3703 | |
3704 | json_stack_release(current); | |
3705 | n_stack--, current--; | |
3706 | ||
3707 | break; | |
3708 | ||
3709 | case _JSON_BUILD_STRV: { | |
3710 | char **l; | |
3711 | ||
3712 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3713 | r = -EINVAL; | |
3714 | goto finish; | |
3715 | } | |
3716 | ||
3717 | l = va_arg(ap, char **); | |
3718 | ||
319a4f27 LP |
3719 | if (current->n_suppress == 0) { |
3720 | r = json_variant_new_array_strv(&add, l); | |
3721 | if (r < 0) | |
3722 | goto finish; | |
3723 | } | |
3724 | ||
3725 | n_subtract = 1; | |
08e29ac7 LP |
3726 | |
3727 | if (current->expect == EXPECT_TOPLEVEL) | |
3728 | current->expect = EXPECT_END; | |
3729 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3730 | current->expect = EXPECT_OBJECT_KEY; | |
3731 | else | |
3732 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3733 | ||
3734 | break; | |
3735 | } | |
3736 | ||
3737 | case _JSON_BUILD_STRV_ENV_PAIR: { | |
3738 | char **l; | |
3739 | ||
3740 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3741 | r = -EINVAL; | |
3742 | goto finish; | |
3743 | } | |
3744 | ||
3745 | l = va_arg(ap, char **); | |
3746 | ||
3747 | _cleanup_strv_free_ char **el = NULL; | |
3748 | STRV_FOREACH_PAIR(x, y, l) { | |
3749 | char *n = NULL; | |
3750 | ||
3751 | n = strjoin(*x, "=", *y); | |
3752 | if (!n) { | |
3753 | r = -ENOMEM; | |
3754 | goto finish; | |
3755 | } | |
3756 | ||
3757 | r = strv_consume(&el, n); | |
3758 | if (r < 0) | |
3759 | goto finish; | |
3760 | } | |
3761 | ||
3762 | if (current->n_suppress == 0) { | |
3763 | r = json_variant_new_array_strv(&add, el); | |
3764 | if (r < 0) | |
3765 | goto finish; | |
3766 | } | |
3767 | ||
3768 | n_subtract = 1; | |
cd0b6c53 LP |
3769 | |
3770 | if (current->expect == EXPECT_TOPLEVEL) | |
3771 | current->expect = EXPECT_END; | |
3772 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3773 | current->expect = EXPECT_OBJECT_KEY; | |
3774 | else | |
3775 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3776 | ||
3777 | break; | |
3778 | } | |
886b0c93 | 3779 | |
84738d86 LP |
3780 | case _JSON_BUILD_BASE64: |
3781 | case _JSON_BUILD_BASE32HEX: | |
3782 | case _JSON_BUILD_HEX: | |
3783 | case _JSON_BUILD_OCTESCAPE: { | |
2d64d2b9 LP |
3784 | const void *p; |
3785 | size_t n; | |
3786 | ||
3787 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3788 | r = -EINVAL; | |
3789 | goto finish; | |
3790 | } | |
3791 | ||
3792 | p = va_arg(ap, const void *); | |
3793 | n = va_arg(ap, size_t); | |
3794 | ||
3795 | if (current->n_suppress == 0) { | |
84738d86 LP |
3796 | r = command == _JSON_BUILD_BASE64 ? json_variant_new_base64(&add, p, n) : |
3797 | command == _JSON_BUILD_BASE32HEX ? json_variant_new_base32hex(&add, p, n) : | |
3798 | command == _JSON_BUILD_HEX ? json_variant_new_hex(&add, p, n) : | |
3799 | json_variant_new_octescape(&add, p, n); | |
2d64d2b9 LP |
3800 | if (r < 0) |
3801 | goto finish; | |
3802 | } | |
3803 | ||
3804 | n_subtract = 1; | |
3805 | ||
3806 | if (current->expect == EXPECT_TOPLEVEL) | |
3807 | current->expect = EXPECT_END; | |
3808 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3809 | current->expect = EXPECT_OBJECT_KEY; | |
3810 | else | |
3811 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3812 | ||
3813 | break; | |
3814 | } | |
3815 | ||
b22f0a50 LP |
3816 | case _JSON_BUILD_IOVEC_BASE64: |
3817 | case _JSON_BUILD_IOVEC_HEX: { | |
03427720 LP |
3818 | const struct iovec *iov; |
3819 | ||
3820 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3821 | r = -EINVAL; | |
3822 | goto finish; | |
3823 | } | |
3824 | ||
b22f0a50 | 3825 | iov = va_arg(ap, const struct iovec*); |
03427720 LP |
3826 | |
3827 | if (current->n_suppress == 0) { | |
b22f0a50 LP |
3828 | if (iov) |
3829 | r = command == _JSON_BUILD_IOVEC_BASE64 ? json_variant_new_base64(&add, iov->iov_base, iov->iov_len) : | |
3830 | json_variant_new_hex(&add, iov->iov_base, iov->iov_len); | |
3831 | else | |
3832 | r = json_variant_new_string(&add, ""); | |
03427720 LP |
3833 | if (r < 0) |
3834 | goto finish; | |
3835 | } | |
3836 | ||
3837 | n_subtract = 1; | |
3838 | ||
3839 | if (current->expect == EXPECT_TOPLEVEL) | |
3840 | current->expect = EXPECT_END; | |
3841 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3842 | current->expect = EXPECT_OBJECT_KEY; | |
3843 | else | |
3844 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3845 | ||
3846 | break; | |
3847 | } | |
3848 | ||
9bc4156c LP |
3849 | case _JSON_BUILD_ID128: |
3850 | case _JSON_BUILD_UUID: { | |
3e4ca394 | 3851 | const sd_id128_t *id; |
0b9481cf LP |
3852 | |
3853 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3854 | r = -EINVAL; | |
3855 | goto finish; | |
3856 | } | |
3857 | ||
3e4ca394 | 3858 | assert_se(id = va_arg(ap, sd_id128_t*)); |
0b9481cf LP |
3859 | |
3860 | if (current->n_suppress == 0) { | |
9bc4156c LP |
3861 | r = command == _JSON_BUILD_ID128 ? |
3862 | json_variant_new_id128(&add, *id) : | |
3863 | json_variant_new_uuid(&add, *id); | |
0b9481cf LP |
3864 | if (r < 0) |
3865 | goto finish; | |
3866 | } | |
3867 | ||
3868 | n_subtract = 1; | |
3869 | ||
3870 | if (current->expect == EXPECT_TOPLEVEL) | |
3871 | current->expect = EXPECT_END; | |
3872 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3873 | current->expect = EXPECT_OBJECT_KEY; | |
3874 | else | |
3875 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
0710343c LP |
3876 | |
3877 | break; | |
3878 | } | |
3879 | ||
3880 | case _JSON_BUILD_BYTE_ARRAY: { | |
3881 | const void *array; | |
3882 | size_t n; | |
3883 | ||
3884 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3885 | r = -EINVAL; | |
3886 | goto finish; | |
3887 | } | |
3888 | ||
3889 | array = va_arg(ap, const void*); | |
3890 | n = va_arg(ap, size_t); | |
3891 | ||
3892 | if (current->n_suppress == 0) { | |
3893 | r = json_variant_new_array_bytes(&add, array, n); | |
3894 | if (r < 0) | |
3895 | goto finish; | |
3896 | } | |
3897 | ||
3898 | n_subtract = 1; | |
3899 | ||
3900 | if (current->expect == EXPECT_TOPLEVEL) | |
3901 | current->expect = EXPECT_END; | |
3902 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3903 | current->expect = EXPECT_OBJECT_KEY; | |
3904 | else | |
3905 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
886b0c93 LP |
3906 | |
3907 | break; | |
3908 | } | |
cd0b6c53 | 3909 | |
7314084a YW |
3910 | case _JSON_BUILD_HW_ADDR: { |
3911 | const struct hw_addr_data *hw_addr; | |
3912 | ||
3913 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3914 | r = -EINVAL; | |
3915 | goto finish; | |
3916 | } | |
3917 | ||
3918 | assert_se(hw_addr = va_arg(ap, struct hw_addr_data*)); | |
3919 | ||
3920 | if (current->n_suppress == 0) { | |
3921 | r = json_variant_new_array_bytes(&add, hw_addr->bytes, hw_addr->length); | |
3922 | if (r < 0) | |
3923 | goto finish; | |
3924 | } | |
3925 | ||
3926 | n_subtract = 1; | |
3927 | ||
3928 | if (current->expect == EXPECT_TOPLEVEL) | |
3929 | current->expect = EXPECT_END; | |
3930 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3931 | current->expect = EXPECT_OBJECT_KEY; | |
3932 | else | |
3933 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3934 | ||
3935 | break; | |
3936 | } | |
3937 | ||
a636a058 DDM |
3938 | case _JSON_BUILD_STRING_SET: { |
3939 | Set *set; | |
3940 | ||
3941 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3942 | r = -EINVAL; | |
3943 | goto finish; | |
3944 | } | |
3945 | ||
3946 | set = va_arg(ap, Set*); | |
3947 | ||
3948 | if (current->n_suppress == 0) { | |
3949 | _cleanup_free_ char **sorted = NULL; | |
3950 | ||
3951 | r = set_dump_sorted(set, (void ***) &sorted, NULL); | |
3952 | if (r < 0) | |
3953 | goto finish; | |
3954 | ||
3955 | r = json_variant_new_array_strv(&add, sorted); | |
3956 | if (r < 0) | |
3957 | goto finish; | |
3958 | } | |
3959 | ||
3960 | n_subtract = 1; | |
3961 | ||
3962 | if (current->expect == EXPECT_TOPLEVEL) | |
3963 | current->expect = EXPECT_END; | |
3964 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
3965 | current->expect = EXPECT_OBJECT_KEY; | |
3966 | else | |
3967 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
3968 | ||
3969 | break; | |
3970 | } | |
3971 | ||
8eb735b8 DDM |
3972 | case _JSON_BUILD_CALLBACK: { |
3973 | JsonBuildCallback cb; | |
3974 | void *userdata; | |
3975 | ||
3976 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
3977 | r = -EINVAL; | |
3978 | goto finish; | |
3979 | } | |
3980 | ||
3981 | cb = va_arg(ap, JsonBuildCallback); | |
3982 | userdata = va_arg(ap, void *); | |
3983 | ||
3984 | if (current->n_suppress == 0) { | |
3985 | if (cb) { | |
3986 | r = cb(&add, name, userdata); | |
3987 | if (r < 0) | |
3988 | goto finish; | |
3989 | } | |
3990 | ||
3991 | if (!add) | |
3992 | add = JSON_VARIANT_MAGIC_NULL; | |
3993 | ||
3994 | name = NULL; | |
3995 | } | |
3996 | ||
3997 | n_subtract = 1; | |
3998 | ||
3999 | if (current->expect == EXPECT_TOPLEVEL) | |
4000 | current->expect = EXPECT_END; | |
4001 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
4002 | current->expect = EXPECT_OBJECT_KEY; | |
4003 | else | |
4004 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
4005 | ||
4006 | break; | |
4007 | } | |
4008 | ||
cd0b6c53 LP |
4009 | case _JSON_BUILD_OBJECT_BEGIN: |
4010 | ||
4011 | if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { | |
4012 | r = -EINVAL; | |
4013 | goto finish; | |
4014 | } | |
4015 | ||
319a4f4b | 4016 | if (!GREEDY_REALLOC(stack, n_stack+1)) { |
cd0b6c53 LP |
4017 | r = -ENOMEM; |
4018 | goto finish; | |
4019 | } | |
4020 | current = stack + n_stack - 1; | |
4021 | ||
4022 | if (current->expect == EXPECT_TOPLEVEL) | |
4023 | current->expect = EXPECT_END; | |
4024 | else if (current->expect == EXPECT_OBJECT_VALUE) | |
4025 | current->expect = EXPECT_OBJECT_KEY; | |
4026 | else | |
4027 | assert(current->expect == EXPECT_ARRAY_ELEMENT); | |
4028 | ||
4029 | stack[n_stack++] = (JsonStack) { | |
4030 | .expect = EXPECT_OBJECT_KEY, | |
bac06497 ZJS |
4031 | .n_suppress = current->n_suppress != 0 ? SIZE_MAX : 0, /* If we shall suppress the |
4032 | * new object, then we should | |
4033 | * also suppress all object | |
4034 | * members. */ | |
cd0b6c53 LP |
4035 | }; |
4036 | ||
4037 | break; | |
4038 | ||
4039 | case _JSON_BUILD_OBJECT_END: | |
4040 | ||
4041 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4042 | r = -EINVAL; | |
4043 | goto finish; | |
4044 | } | |
4045 | ||
4046 | assert(n_stack > 1); | |
4047 | ||
319a4f27 LP |
4048 | if (current->n_suppress == 0) { |
4049 | r = json_variant_new_object(&add, current->elements, current->n_elements); | |
4050 | if (r < 0) | |
4051 | goto finish; | |
4052 | } | |
4053 | ||
4054 | n_subtract = 1; | |
cd0b6c53 LP |
4055 | |
4056 | json_stack_release(current); | |
4057 | n_stack--, current--; | |
4058 | ||
4059 | break; | |
4060 | ||
4061 | case _JSON_BUILD_PAIR: { | |
cd0b6c53 LP |
4062 | |
4063 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4064 | r = -EINVAL; | |
4065 | goto finish; | |
4066 | } | |
4067 | ||
8eb735b8 | 4068 | name = va_arg(ap, const char *); |
cd0b6c53 | 4069 | |
319a4f27 | 4070 | if (current->n_suppress == 0) { |
8eb735b8 | 4071 | r = json_variant_new_string(&add, name); |
319a4f27 LP |
4072 | if (r < 0) |
4073 | goto finish; | |
4074 | } | |
4075 | ||
4076 | n_subtract = 1; | |
4077 | ||
4078 | current->expect = EXPECT_OBJECT_VALUE; | |
4079 | break; | |
4080 | } | |
4081 | ||
4082 | case _JSON_BUILD_PAIR_CONDITION: { | |
319a4f27 LP |
4083 | bool b; |
4084 | ||
4085 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4086 | r = -EINVAL; | |
cd0b6c53 | 4087 | goto finish; |
319a4f27 LP |
4088 | } |
4089 | ||
4090 | b = va_arg(ap, int); | |
8eb735b8 | 4091 | name = va_arg(ap, const char *); |
319a4f27 LP |
4092 | |
4093 | if (b && current->n_suppress == 0) { | |
8eb735b8 | 4094 | r = json_variant_new_string(&add, name); |
319a4f27 LP |
4095 | if (r < 0) |
4096 | goto finish; | |
4097 | } | |
4098 | ||
4099 | n_subtract = 1; /* we generated one item */ | |
4100 | ||
f5fbe71d | 4101 | if (!b && current->n_suppress != SIZE_MAX) |
319a4f27 | 4102 | current->n_suppress += 2; /* Suppress this one and the next item */ |
cd0b6c53 LP |
4103 | |
4104 | current->expect = EXPECT_OBJECT_VALUE; | |
4105 | break; | |
7314084a YW |
4106 | } |
4107 | ||
4108 | case _JSON_BUILD_PAIR_UNSIGNED_NON_ZERO: { | |
4109 | const char *n; | |
4110 | uint64_t u; | |
4111 | ||
4112 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4113 | r = -EINVAL; | |
4114 | goto finish; | |
4115 | } | |
4116 | ||
4117 | n = va_arg(ap, const char *); | |
4118 | u = va_arg(ap, uint64_t); | |
4119 | ||
4120 | if (u != 0 && current->n_suppress == 0) { | |
4121 | r = json_variant_new_string(&add, n); | |
4122 | if (r < 0) | |
4123 | goto finish; | |
4124 | ||
4125 | r = json_variant_new_unsigned(&add_more, u); | |
4126 | if (r < 0) | |
4127 | goto finish; | |
4128 | } | |
4129 | ||
4130 | n_subtract = 2; /* we generated two item */ | |
4131 | ||
4132 | current->expect = EXPECT_OBJECT_KEY; | |
4133 | break; | |
4134 | } | |
4135 | ||
4136 | case _JSON_BUILD_PAIR_FINITE_USEC: { | |
4137 | const char *n; | |
4138 | usec_t u; | |
4139 | ||
4140 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4141 | r = -EINVAL; | |
4142 | goto finish; | |
4143 | } | |
4144 | ||
4145 | n = va_arg(ap, const char *); | |
4146 | u = va_arg(ap, usec_t); | |
4147 | ||
4148 | if (u != USEC_INFINITY && current->n_suppress == 0) { | |
4149 | r = json_variant_new_string(&add, n); | |
4150 | if (r < 0) | |
4151 | goto finish; | |
cd0b6c53 | 4152 | |
7314084a YW |
4153 | r = json_variant_new_unsigned(&add_more, u); |
4154 | if (r < 0) | |
4155 | goto finish; | |
4156 | } | |
4157 | ||
4158 | n_subtract = 2; /* we generated two item */ | |
4159 | ||
4160 | current->expect = EXPECT_OBJECT_KEY; | |
4161 | break; | |
4162 | } | |
4163 | ||
4164 | case _JSON_BUILD_PAIR_STRING_NON_EMPTY: { | |
4165 | const char *n, *s; | |
4166 | ||
4167 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4168 | r = -EINVAL; | |
4169 | goto finish; | |
4170 | } | |
4171 | ||
4172 | n = va_arg(ap, const char *); | |
4173 | s = va_arg(ap, const char *); | |
4174 | ||
4175 | if (!isempty(s) && current->n_suppress == 0) { | |
4176 | r = json_variant_new_string(&add, n); | |
4177 | if (r < 0) | |
4178 | goto finish; | |
4179 | ||
4180 | r = json_variant_new_string(&add_more, s); | |
4181 | if (r < 0) | |
4182 | goto finish; | |
4183 | } | |
4184 | ||
4185 | n_subtract = 2; /* we generated two item */ | |
4186 | ||
4187 | current->expect = EXPECT_OBJECT_KEY; | |
4188 | break; | |
4189 | } | |
4190 | ||
4191 | case _JSON_BUILD_PAIR_STRV_NON_EMPTY: { | |
4192 | const char *n; | |
4193 | char **l; | |
4194 | ||
4195 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4196 | r = -EINVAL; | |
4197 | goto finish; | |
4198 | } | |
4199 | ||
4200 | n = va_arg(ap, const char *); | |
4201 | l = va_arg(ap, char **); | |
4202 | ||
4203 | if (!strv_isempty(l) && current->n_suppress == 0) { | |
4204 | r = json_variant_new_string(&add, n); | |
4205 | if (r < 0) | |
4206 | goto finish; | |
4207 | ||
4208 | r = json_variant_new_array_strv(&add_more, l); | |
4209 | if (r < 0) | |
4210 | goto finish; | |
4211 | } | |
4212 | ||
4213 | n_subtract = 2; /* we generated two item */ | |
4214 | ||
4215 | current->expect = EXPECT_OBJECT_KEY; | |
4216 | break; | |
4217 | } | |
4218 | ||
4219 | case _JSON_BUILD_PAIR_VARIANT_NON_NULL: { | |
4220 | JsonVariant *v; | |
4221 | const char *n; | |
4222 | ||
4223 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4224 | r = -EINVAL; | |
4225 | goto finish; | |
4226 | } | |
4227 | ||
4228 | n = va_arg(ap, const char *); | |
4229 | v = va_arg(ap, JsonVariant *); | |
4230 | ||
4231 | if (v && !json_variant_is_null(v) && current->n_suppress == 0) { | |
4232 | r = json_variant_new_string(&add, n); | |
4233 | if (r < 0) | |
4234 | goto finish; | |
4235 | ||
4236 | add_more = json_variant_ref(v); | |
4237 | } | |
4238 | ||
4239 | n_subtract = 2; /* we generated two item */ | |
4240 | ||
4241 | current->expect = EXPECT_OBJECT_KEY; | |
4242 | break; | |
4243 | } | |
4244 | ||
4245 | case _JSON_BUILD_PAIR_IN4_ADDR_NON_NULL: { | |
4246 | const struct in_addr *a; | |
4247 | const char *n; | |
4248 | ||
4249 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4250 | r = -EINVAL; | |
4251 | goto finish; | |
4252 | } | |
4253 | ||
4254 | n = va_arg(ap, const char *); | |
4255 | a = va_arg(ap, const struct in_addr *); | |
4256 | ||
b89931ba | 4257 | if (a && in4_addr_is_set(a) && current->n_suppress == 0) { |
7314084a YW |
4258 | r = json_variant_new_string(&add, n); |
4259 | if (r < 0) | |
4260 | goto finish; | |
4261 | ||
4262 | r = json_variant_new_array_bytes(&add_more, a, sizeof(struct in_addr)); | |
4263 | if (r < 0) | |
4264 | goto finish; | |
4265 | } | |
4266 | ||
4267 | n_subtract = 2; /* we generated two item */ | |
4268 | ||
4269 | current->expect = EXPECT_OBJECT_KEY; | |
4270 | break; | |
4271 | } | |
4272 | ||
4273 | case _JSON_BUILD_PAIR_IN6_ADDR_NON_NULL: { | |
4274 | const struct in6_addr *a; | |
4275 | const char *n; | |
4276 | ||
4277 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4278 | r = -EINVAL; | |
4279 | goto finish; | |
4280 | } | |
4281 | ||
4282 | n = va_arg(ap, const char *); | |
4283 | a = va_arg(ap, const struct in6_addr *); | |
4284 | ||
b89931ba | 4285 | if (a && in6_addr_is_set(a) && current->n_suppress == 0) { |
7314084a YW |
4286 | r = json_variant_new_string(&add, n); |
4287 | if (r < 0) | |
4288 | goto finish; | |
4289 | ||
4290 | r = json_variant_new_array_bytes(&add_more, a, sizeof(struct in6_addr)); | |
4291 | if (r < 0) | |
4292 | goto finish; | |
4293 | } | |
4294 | ||
4295 | n_subtract = 2; /* we generated two item */ | |
4296 | ||
4297 | current->expect = EXPECT_OBJECT_KEY; | |
4298 | break; | |
4299 | } | |
4300 | ||
4301 | case _JSON_BUILD_PAIR_IN_ADDR_NON_NULL: { | |
4302 | const union in_addr_union *a; | |
4303 | const char *n; | |
4304 | int f; | |
4305 | ||
4306 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4307 | r = -EINVAL; | |
4308 | goto finish; | |
4309 | } | |
4310 | ||
4311 | n = va_arg(ap, const char *); | |
4312 | a = va_arg(ap, const union in_addr_union *); | |
4313 | f = va_arg(ap, int); | |
4314 | ||
b89931ba | 4315 | if (a && in_addr_is_set(f, a) && current->n_suppress == 0) { |
7314084a YW |
4316 | r = json_variant_new_string(&add, n); |
4317 | if (r < 0) | |
4318 | goto finish; | |
4319 | ||
4320 | r = json_variant_new_array_bytes(&add_more, a->bytes, FAMILY_ADDRESS_SIZE(f)); | |
4321 | if (r < 0) | |
4322 | goto finish; | |
4323 | } | |
4324 | ||
4325 | n_subtract = 2; /* we generated two item */ | |
4326 | ||
4327 | current->expect = EXPECT_OBJECT_KEY; | |
4328 | break; | |
4329 | } | |
4330 | ||
4331 | case _JSON_BUILD_PAIR_ETHER_ADDR_NON_NULL: { | |
4332 | const struct ether_addr *a; | |
4333 | const char *n; | |
4334 | ||
4335 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4336 | r = -EINVAL; | |
4337 | goto finish; | |
4338 | } | |
4339 | ||
4340 | n = va_arg(ap, const char *); | |
4341 | a = va_arg(ap, const struct ether_addr *); | |
4342 | ||
b89931ba | 4343 | if (a && !ether_addr_is_null(a) && current->n_suppress == 0) { |
7314084a YW |
4344 | r = json_variant_new_string(&add, n); |
4345 | if (r < 0) | |
4346 | goto finish; | |
4347 | ||
4348 | r = json_variant_new_array_bytes(&add_more, a->ether_addr_octet, sizeof(struct ether_addr)); | |
4349 | if (r < 0) | |
4350 | goto finish; | |
4351 | } | |
4352 | ||
4353 | n_subtract = 2; /* we generated two item */ | |
4354 | ||
4355 | current->expect = EXPECT_OBJECT_KEY; | |
4356 | break; | |
4357 | } | |
4358 | ||
4359 | case _JSON_BUILD_PAIR_HW_ADDR_NON_NULL: { | |
4360 | const struct hw_addr_data *a; | |
4361 | const char *n; | |
4362 | ||
4363 | if (current->expect != EXPECT_OBJECT_KEY) { | |
4364 | r = -EINVAL; | |
4365 | goto finish; | |
4366 | } | |
4367 | ||
4368 | n = va_arg(ap, const char *); | |
4369 | a = va_arg(ap, const struct hw_addr_data *); | |
4370 | ||
b89931ba | 4371 | if (a && !hw_addr_is_null(a) && current->n_suppress == 0) { |
7314084a YW |
4372 | r = json_variant_new_string(&add, n); |
4373 | if (r < 0) | |
4374 | goto finish; | |
4375 | ||
4376 | r = json_variant_new_array_bytes(&add_more, a->bytes, a->length); | |
4377 | if (r < 0) | |
4378 | goto finish; | |
4379 | } | |
4380 | ||
4381 | n_subtract = 2; /* we generated two item */ | |
4382 | ||
4383 | current->expect = EXPECT_OBJECT_KEY; | |
4384 | break; | |
4385 | } | |
4386 | } | |
4387 | ||
4388 | /* If variants were generated, add them to our current variant, but only if we are not supposed to suppress additions */ | |
319a4f27 | 4389 | if (add && current->n_suppress == 0) { |
7314084a | 4390 | if (!GREEDY_REALLOC(current->elements, current->n_elements + 1 + !!add_more)) { |
cd0b6c53 LP |
4391 | r = -ENOMEM; |
4392 | goto finish; | |
4393 | } | |
4394 | ||
fcadf032 | 4395 | current->elements[current->n_elements++] = TAKE_PTR(add); |
7314084a YW |
4396 | if (add_more) |
4397 | current->elements[current->n_elements++] = TAKE_PTR(add_more); | |
cd0b6c53 | 4398 | } |
319a4f27 | 4399 | |
bac06497 ZJS |
4400 | /* If we are supposed to suppress items, let's subtract how many items where generated from |
4401 | * that counter. Except if the counter is SIZE_MAX, i.e. we shall suppress an infinite number | |
4402 | * of elements on this stack level */ | |
f5fbe71d | 4403 | if (current->n_suppress != SIZE_MAX) { |
319a4f27 LP |
4404 | if (current->n_suppress <= n_subtract) /* Saturated */ |
4405 | current->n_suppress = 0; | |
4406 | else | |
4407 | current->n_suppress -= n_subtract; | |
4408 | } | |
cd0b6c53 LP |
4409 | } |
4410 | ||
4411 | done: | |
4412 | assert(n_stack == 1); | |
4413 | assert(stack[0].n_elements == 1); | |
4414 | ||
4415 | *ret = json_variant_ref(stack[0].elements[0]); | |
4416 | r = 0; | |
4417 | ||
4418 | finish: | |
a4669764 | 4419 | for (size_t i = 0; i < n_stack; i++) |
cd0b6c53 LP |
4420 | json_stack_release(stack + i); |
4421 | ||
4422 | free(stack); | |
4423 | ||
cd0b6c53 LP |
4424 | return r; |
4425 | } | |
4426 | ||
4427 | int json_build(JsonVariant **ret, ...) { | |
4428 | va_list ap; | |
4429 | int r; | |
4430 | ||
4431 | va_start(ap, ret); | |
4432 | r = json_buildv(ret, ap); | |
4433 | va_end(ap); | |
4434 | ||
4435 | return r; | |
4436 | } | |
4437 | ||
4438 | int json_log_internal( | |
4439 | JsonVariant *variant, | |
4440 | int level, | |
4441 | int error, | |
4442 | const char *file, | |
4443 | int line, | |
4444 | const char *func, | |
4445 | const char *format, ...) { | |
4446 | ||
4447 | PROTECT_ERRNO; | |
4448 | ||
4449 | unsigned source_line, source_column; | |
4450 | char buffer[LINE_MAX]; | |
4451 | const char *source; | |
4452 | va_list ap; | |
4453 | int r; | |
4454 | ||
fc0f6fbf | 4455 | errno = ERRNO_VALUE(error); |
cd0b6c53 LP |
4456 | |
4457 | va_start(ap, format); | |
4458 | (void) vsnprintf(buffer, sizeof buffer, format, ap); | |
4459 | va_end(ap); | |
4460 | ||
4461 | if (variant) { | |
4462 | r = json_variant_get_source(variant, &source, &source_line, &source_column); | |
4463 | if (r < 0) | |
4464 | return r; | |
4465 | } else { | |
4466 | source = NULL; | |
4467 | source_line = 0; | |
4468 | source_column = 0; | |
4469 | } | |
4470 | ||
4471 | if (source && source_line > 0 && source_column > 0) | |
4472 | return log_struct_internal( | |
9fdee66f | 4473 | level, |
cd0b6c53 LP |
4474 | error, |
4475 | file, line, func, | |
4476 | "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR, | |
4477 | "CONFIG_FILE=%s", source, | |
4478 | "CONFIG_LINE=%u", source_line, | |
4479 | "CONFIG_COLUMN=%u", source_column, | |
13a16a2b | 4480 | LOG_MESSAGE("%s:%u:%u: %s", source, source_line, source_column, buffer), |
cd0b6c53 | 4481 | NULL); |
d401a5e4 ZJS |
4482 | else if (source_line > 0 && source_column > 0) |
4483 | return log_struct_internal( | |
9fdee66f | 4484 | level, |
d401a5e4 ZJS |
4485 | error, |
4486 | file, line, func, | |
4487 | "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR, | |
4488 | "CONFIG_LINE=%u", source_line, | |
4489 | "CONFIG_COLUMN=%u", source_column, | |
4490 | LOG_MESSAGE("(string):%u:%u: %s", source_line, source_column, buffer), | |
4491 | NULL); | |
cd0b6c53 LP |
4492 | else |
4493 | return log_struct_internal( | |
9fdee66f | 4494 | level, |
cd0b6c53 LP |
4495 | error, |
4496 | file, line, func, | |
4497 | "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR, | |
4498 | LOG_MESSAGE("%s", buffer), | |
4499 | NULL); | |
4500 | } | |
4501 | ||
0b8218b9 LP |
4502 | static void *dispatch_userdata(const JsonDispatch *p, void *userdata) { |
4503 | ||
0b75493d | 4504 | /* When the userdata pointer is passed in as NULL, then we'll just use the offset as a literal |
0b8218b9 LP |
4505 | * address, and convert it to a pointer. Note that might as well just add the offset to the NULL |
4506 | * pointer, but UndefinedBehaviourSanitizer doesn't like pointer arithmetics based on NULL pointers, | |
4507 | * hence we code this explicitly here. */ | |
4508 | ||
4509 | if (userdata) | |
4510 | return (uint8_t*) userdata + p->offset; | |
4511 | ||
4512 | return SIZE_TO_PTR(p->offset); | |
4513 | } | |
4514 | ||
f1b622a0 LP |
4515 | int json_dispatch_full( |
4516 | JsonVariant *v, | |
4517 | const JsonDispatch table[], | |
4518 | JsonDispatchCallback bad, | |
4519 | JsonDispatchFlags flags, | |
4520 | void *userdata, | |
4521 | const char **reterr_bad_field) { | |
a4669764 | 4522 | size_t m; |
cd0b6c53 LP |
4523 | int r, done = 0; |
4524 | bool *found; | |
4525 | ||
4526 | if (!json_variant_is_object(v)) { | |
4527 | json_log(v, flags, 0, "JSON variant is not an object."); | |
4528 | ||
4529 | if (flags & JSON_PERMISSIVE) | |
4530 | return 0; | |
4531 | ||
f1b622a0 LP |
4532 | if (reterr_bad_field) |
4533 | *reterr_bad_field = NULL; | |
4534 | ||
cd0b6c53 LP |
4535 | return -EINVAL; |
4536 | } | |
4537 | ||
a4669764 ZJS |
4538 | m = 0; |
4539 | for (const JsonDispatch *p = table; p->name; p++) | |
cd0b6c53 LP |
4540 | m++; |
4541 | ||
4542 | found = newa0(bool, m); | |
4543 | ||
a4669764 ZJS |
4544 | size_t n = json_variant_elements(v); |
4545 | for (size_t i = 0; i < n; i += 2) { | |
cd0b6c53 | 4546 | JsonVariant *key, *value; |
a4669764 | 4547 | const JsonDispatch *p; |
cd0b6c53 LP |
4548 | |
4549 | assert_se(key = json_variant_by_index(v, i)); | |
4550 | assert_se(value = json_variant_by_index(v, i+1)); | |
4551 | ||
4552 | for (p = table; p->name; p++) | |
66032ef4 | 4553 | if (p->name == POINTER_MAX || |
cd0b6c53 LP |
4554 | streq_ptr(json_variant_string(key), p->name)) |
4555 | break; | |
4556 | ||
f1b622a0 | 4557 | if (p->name) { /* Found a matching entry! 🙂 */ |
cd0b6c53 LP |
4558 | JsonDispatchFlags merged_flags; |
4559 | ||
4560 | merged_flags = flags | p->flags; | |
4561 | ||
4562 | if (p->type != _JSON_VARIANT_TYPE_INVALID && | |
4563 | !json_variant_has_type(value, p->type)) { | |
4564 | ||
4565 | json_log(value, merged_flags, 0, | |
4566 | "Object field '%s' has wrong type %s, expected %s.", json_variant_string(key), | |
4567 | json_variant_type_to_string(json_variant_type(value)), json_variant_type_to_string(p->type)); | |
4568 | ||
4569 | if (merged_flags & JSON_PERMISSIVE) | |
4570 | continue; | |
4571 | ||
f1b622a0 LP |
4572 | if (reterr_bad_field) |
4573 | *reterr_bad_field = p->name; | |
4574 | ||
cd0b6c53 LP |
4575 | return -EINVAL; |
4576 | } | |
4577 | ||
4578 | if (found[p-table]) { | |
4579 | json_log(value, merged_flags, 0, "Duplicate object field '%s'.", json_variant_string(key)); | |
4580 | ||
4581 | if (merged_flags & JSON_PERMISSIVE) | |
4582 | continue; | |
4583 | ||
f1b622a0 LP |
4584 | if (reterr_bad_field) |
4585 | *reterr_bad_field = p->name; | |
4586 | ||
cd0b6c53 LP |
4587 | return -ENOTUNIQ; |
4588 | } | |
4589 | ||
4590 | found[p-table] = true; | |
4591 | ||
4592 | if (p->callback) { | |
0b8218b9 | 4593 | r = p->callback(json_variant_string(key), value, merged_flags, dispatch_userdata(p, userdata)); |
cd0b6c53 LP |
4594 | if (r < 0) { |
4595 | if (merged_flags & JSON_PERMISSIVE) | |
4596 | continue; | |
4597 | ||
f1b622a0 LP |
4598 | if (reterr_bad_field) |
4599 | *reterr_bad_field = json_variant_string(key); | |
4600 | ||
cd0b6c53 LP |
4601 | return r; |
4602 | } | |
4603 | } | |
4604 | ||
b3a9d980 | 4605 | done++; |
cd0b6c53 | 4606 | |
f1b622a0 | 4607 | } else { /* Didn't find a matching entry! ☹️ */ |
cd0b6c53 LP |
4608 | |
4609 | if (bad) { | |
4610 | r = bad(json_variant_string(key), value, flags, userdata); | |
4611 | if (r < 0) { | |
4612 | if (flags & JSON_PERMISSIVE) | |
4613 | continue; | |
4614 | ||
f1b622a0 LP |
4615 | if (reterr_bad_field) |
4616 | *reterr_bad_field = json_variant_string(key); | |
4617 | ||
cd0b6c53 LP |
4618 | return r; |
4619 | } else | |
b3a9d980 | 4620 | done++; |
cd0b6c53 LP |
4621 | |
4622 | } else { | |
a617fd90 LP |
4623 | if (flags & JSON_ALLOW_EXTENSIONS) { |
4624 | json_log(value, flags, 0, "Unrecognized object field '%s', assuming extension.", json_variant_string(key)); | |
4625 | continue; | |
4626 | } | |
cd0b6c53 | 4627 | |
a617fd90 | 4628 | json_log(value, flags, 0, "Unexpected object field '%s'.", json_variant_string(key)); |
cd0b6c53 LP |
4629 | if (flags & JSON_PERMISSIVE) |
4630 | continue; | |
4631 | ||
f1b622a0 LP |
4632 | if (reterr_bad_field) |
4633 | *reterr_bad_field = json_variant_string(key); | |
4634 | ||
cd0b6c53 LP |
4635 | return -EADDRNOTAVAIL; |
4636 | } | |
4637 | } | |
4638 | } | |
4639 | ||
a4669764 | 4640 | for (const JsonDispatch *p = table; p->name; p++) { |
cd0b6c53 LP |
4641 | JsonDispatchFlags merged_flags = p->flags | flags; |
4642 | ||
4643 | if ((merged_flags & JSON_MANDATORY) && !found[p-table]) { | |
4644 | json_log(v, merged_flags, 0, "Missing object field '%s'.", p->name); | |
4645 | ||
4646 | if ((merged_flags & JSON_PERMISSIVE)) | |
4647 | continue; | |
4648 | ||
f1b622a0 LP |
4649 | if (reterr_bad_field) |
4650 | *reterr_bad_field = p->name; | |
4651 | ||
cd0b6c53 LP |
4652 | return -ENXIO; |
4653 | } | |
4654 | } | |
4655 | ||
4656 | return done; | |
4657 | } | |
4658 | ||
4659 | int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
99534007 | 4660 | bool *b = ASSERT_PTR(userdata); |
cd0b6c53 LP |
4661 | |
4662 | assert(variant); | |
cd0b6c53 | 4663 | |
02dab76e LP |
4664 | if (!json_variant_is_boolean(variant)) |
4665 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name)); | |
cd0b6c53 LP |
4666 | |
4667 | *b = json_variant_boolean(variant); | |
4668 | return 0; | |
4669 | } | |
4670 | ||
4671 | int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
99534007 | 4672 | int *b = ASSERT_PTR(userdata); |
cd0b6c53 LP |
4673 | |
4674 | assert(variant); | |
cd0b6c53 | 4675 | |
3dd1b600 LP |
4676 | if (json_variant_is_null(variant)) { |
4677 | *b = -1; | |
4678 | return 0; | |
4679 | } | |
4680 | ||
02dab76e LP |
4681 | if (!json_variant_is_boolean(variant)) |
4682 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name)); | |
cd0b6c53 LP |
4683 | |
4684 | *b = json_variant_boolean(variant); | |
4685 | return 0; | |
4686 | } | |
4687 | ||
718ca772 | 4688 | int json_dispatch_int64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
99534007 | 4689 | int64_t *i = ASSERT_PTR(userdata); |
cd0b6c53 LP |
4690 | |
4691 | assert(variant); | |
cd0b6c53 | 4692 | |
67a30285 LP |
4693 | /* Also accept numbers formatted as string, to increase compatibility with less capable JSON |
4694 | * implementations that cannot do 64bit integers. */ | |
4695 | if (json_variant_is_string(variant) && safe_atoi64(json_variant_string(variant), i) >= 0) | |
4696 | return 0; | |
4697 | ||
02dab76e | 4698 | if (!json_variant_is_integer(variant)) |
67a30285 | 4699 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer, nor one formatted as decimal string.", strna(name)); |
cd0b6c53 LP |
4700 | |
4701 | *i = json_variant_integer(variant); | |
4702 | return 0; | |
4703 | } | |
4704 | ||
718ca772 | 4705 | int json_dispatch_uint64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
99534007 | 4706 | uint64_t *u = ASSERT_PTR(userdata); |
cd0b6c53 LP |
4707 | |
4708 | assert(variant); | |
cd0b6c53 | 4709 | |
67a30285 LP |
4710 | /* Since 64bit values (in particular unsigned ones) in JSON are problematic, let's also accept them |
4711 | * formatted as strings. If this is not desired make sure to set the .type field in JsonDispatch to | |
4712 | * JSON_UNSIGNED rather than _JSON_VARIANT_TYPE_INVALID, so that json_dispatch() already filters out | |
4713 | * the non-matching type. */ | |
4714 | ||
4715 | if (json_variant_is_string(variant) && safe_atou64(json_variant_string(variant), u) >= 0) | |
4716 | return 0; | |
4717 | ||
02dab76e | 4718 | if (!json_variant_is_unsigned(variant)) |
67a30285 | 4719 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer, nor one formatted as decimal string.", strna(name)); |
cd0b6c53 LP |
4720 | |
4721 | *u = json_variant_unsigned(variant); | |
4722 | return 0; | |
4723 | } | |
4724 | ||
4725 | int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
99534007 | 4726 | uint32_t *u = ASSERT_PTR(userdata); |
67a30285 LP |
4727 | uint64_t u64; |
4728 | int r; | |
cd0b6c53 LP |
4729 | |
4730 | assert(variant); | |
cd0b6c53 | 4731 | |
67a30285 LP |
4732 | r = json_dispatch_uint64(name, variant, flags, &u64); |
4733 | if (r < 0) | |
4734 | return r; | |
cd0b6c53 | 4735 | |
67a30285 | 4736 | if (u64 > UINT32_MAX) |
02dab76e | 4737 | return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); |
cd0b6c53 | 4738 | |
67a30285 | 4739 | *u = (uint32_t) u64; |
cd0b6c53 LP |
4740 | return 0; |
4741 | } | |
4742 | ||
4743 | int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
99534007 | 4744 | int32_t *i = ASSERT_PTR(userdata); |
67a30285 LP |
4745 | int64_t i64; |
4746 | int r; | |
cd0b6c53 LP |
4747 | |
4748 | assert(variant); | |
cd0b6c53 | 4749 | |
67a30285 LP |
4750 | r = json_dispatch_int64(name, variant, flags, &i64); |
4751 | if (r < 0) | |
4752 | return r; | |
cd0b6c53 | 4753 | |
67a30285 | 4754 | if (i64 < INT32_MIN || i64 > INT32_MAX) |
02dab76e | 4755 | return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); |
cd0b6c53 | 4756 | |
67a30285 | 4757 | *i = (int32_t) i64; |
cd0b6c53 LP |
4758 | return 0; |
4759 | } | |
4760 | ||
e085625f LP |
4761 | int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
4762 | int16_t *i = ASSERT_PTR(userdata); | |
67a30285 LP |
4763 | int64_t i64; |
4764 | int r; | |
e085625f LP |
4765 | |
4766 | assert(variant); | |
4767 | ||
67a30285 LP |
4768 | r = json_dispatch_int64(name, variant, flags, &i64); |
4769 | if (r < 0) | |
4770 | return r; | |
e085625f | 4771 | |
67a30285 | 4772 | if (i64 < INT16_MIN || i64 > INT16_MAX) |
e085625f LP |
4773 | return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); |
4774 | ||
67a30285 | 4775 | *i = (int16_t) i64; |
e085625f LP |
4776 | return 0; |
4777 | } | |
4778 | ||
4779 | int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
67a30285 LP |
4780 | uint16_t *u = ASSERT_PTR(userdata); |
4781 | uint64_t u64; | |
4782 | int r; | |
e085625f LP |
4783 | |
4784 | assert(variant); | |
4785 | ||
67a30285 LP |
4786 | r = json_dispatch_uint64(name, variant, flags, &u64); |
4787 | if (r < 0) | |
4788 | return r; | |
e085625f | 4789 | |
67a30285 | 4790 | if (u64 > UINT16_MAX) |
e085625f LP |
4791 | return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); |
4792 | ||
67a30285 | 4793 | *u = (uint16_t) u64; |
e085625f LP |
4794 | return 0; |
4795 | } | |
4796 | ||
cd0b6c53 | 4797 | int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
99534007 | 4798 | char **s = ASSERT_PTR(userdata); |
cd0b6c53 LP |
4799 | int r; |
4800 | ||
4801 | assert(variant); | |
cd0b6c53 LP |
4802 | |
4803 | if (json_variant_is_null(variant)) { | |
4804 | *s = mfree(*s); | |
4805 | return 0; | |
4806 | } | |
4807 | ||
02dab76e LP |
4808 | if (!json_variant_is_string(variant)) |
4809 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); | |
cd0b6c53 | 4810 | |
ba23dbf1 LP |
4811 | if ((flags & JSON_SAFE) && !string_is_safe(json_variant_string(variant))) |
4812 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' contains unsafe characters, refusing.", strna(name)); | |
4813 | ||
cd0b6c53 LP |
4814 | r = free_and_strdup(s, json_variant_string(variant)); |
4815 | if (r < 0) | |
4816 | return json_log(variant, flags, r, "Failed to allocate string: %m"); | |
4817 | ||
4818 | return 0; | |
4819 | } | |
4820 | ||
19a209cc | 4821 | int json_dispatch_const_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
99534007 | 4822 | const char **s = ASSERT_PTR(userdata); |
19a209cc LP |
4823 | |
4824 | assert(variant); | |
19a209cc LP |
4825 | |
4826 | if (json_variant_is_null(variant)) { | |
4827 | *s = NULL; | |
4828 | return 0; | |
4829 | } | |
4830 | ||
4831 | if (!json_variant_is_string(variant)) | |
4832 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); | |
4833 | ||
4834 | if ((flags & JSON_SAFE) && !string_is_safe(json_variant_string(variant))) | |
4835 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' contains unsafe characters, refusing.", strna(name)); | |
4836 | ||
4837 | *s = json_variant_string(variant); | |
4838 | return 0; | |
4839 | } | |
4840 | ||
cd0b6c53 LP |
4841 | int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
4842 | _cleanup_strv_free_ char **l = NULL; | |
99534007 | 4843 | char ***s = ASSERT_PTR(userdata); |
ca5e7694 | 4844 | JsonVariant *e; |
cd0b6c53 LP |
4845 | int r; |
4846 | ||
4847 | assert(variant); | |
cd0b6c53 LP |
4848 | |
4849 | if (json_variant_is_null(variant)) { | |
4850 | *s = strv_free(*s); | |
4851 | return 0; | |
4852 | } | |
4853 | ||
07737617 LP |
4854 | /* Let's be flexible here: accept a single string in place of a single-item array */ |
4855 | if (json_variant_is_string(variant)) { | |
ba23dbf1 LP |
4856 | if ((flags & JSON_SAFE) && !string_is_safe(json_variant_string(variant))) |
4857 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' contains unsafe characters, refusing.", strna(name)); | |
4858 | ||
07737617 LP |
4859 | l = strv_new(json_variant_string(variant)); |
4860 | if (!l) | |
4861 | return log_oom(); | |
4862 | ||
4863 | strv_free_and_replace(*s, l); | |
4864 | return 0; | |
4865 | } | |
4866 | ||
02dab76e LP |
4867 | if (!json_variant_is_array(variant)) |
4868 | return json_log(variant, SYNTHETIC_ERRNO(EINVAL), flags, "JSON field '%s' is not an array.", strna(name)); | |
cd0b6c53 | 4869 | |
ca5e7694 | 4870 | JSON_VARIANT_ARRAY_FOREACH(e, variant) { |
02dab76e LP |
4871 | if (!json_variant_is_string(e)) |
4872 | return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string."); | |
cd0b6c53 | 4873 | |
ba23dbf1 LP |
4874 | if ((flags & JSON_SAFE) && !string_is_safe(json_variant_string(e))) |
4875 | return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' contains unsafe characters, refusing.", strna(name)); | |
4876 | ||
cd0b6c53 LP |
4877 | r = strv_extend(&l, json_variant_string(e)); |
4878 | if (r < 0) | |
b2bdf6e4 | 4879 | return json_log(e, flags, r, "Failed to append array element: %m"); |
cd0b6c53 LP |
4880 | } |
4881 | ||
4882 | strv_free_and_replace(*s, l); | |
4883 | return 0; | |
4884 | } | |
4885 | ||
4886 | int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
ce913e0e | 4887 | JsonVariant **p = ASSERT_PTR(userdata); |
cd0b6c53 | 4888 | assert(variant); |
cd0b6c53 | 4889 | |
6b9f63cb | 4890 | /* Takes a reference */ |
ce913e0e | 4891 | JSON_VARIANT_REPLACE(*p, json_variant_ref(variant)); |
cd0b6c53 LP |
4892 | return 0; |
4893 | } | |
4894 | ||
6b9f63cb LP |
4895 | int json_dispatch_variant_noref(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
4896 | JsonVariant **p = ASSERT_PTR(userdata); | |
4897 | assert(variant); | |
4898 | ||
4899 | /* Doesn't take a reference */ | |
4900 | *p = variant; | |
4901 | return 0; | |
4902 | } | |
4903 | ||
a42ef715 LP |
4904 | int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
4905 | uid_t *uid = userdata; | |
718ca772 | 4906 | uint64_t k; |
a42ef715 LP |
4907 | |
4908 | assert_cc(sizeof(uid_t) == sizeof(uint32_t)); | |
4909 | assert_cc(sizeof(gid_t) == sizeof(uint32_t)); | |
4910 | ||
6028d766 | 4911 | DISABLE_WARNING_TYPE_LIMITS; |
f5fbe71d | 4912 | assert_cc((UID_INVALID < (uid_t) 0) == (GID_INVALID < (gid_t) 0)); |
6028d766 | 4913 | REENABLE_WARNING; |
a42ef715 LP |
4914 | |
4915 | if (json_variant_is_null(variant)) { | |
4916 | *uid = UID_INVALID; | |
4917 | return 0; | |
4918 | } | |
4919 | ||
4920 | if (!json_variant_is_unsigned(variant)) | |
387f6955 | 4921 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); |
a42ef715 LP |
4922 | |
4923 | k = json_variant_unsigned(variant); | |
4924 | if (k > UINT32_MAX || !uid_is_valid(k)) | |
4925 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid UID/GID.", strna(name)); | |
4926 | ||
4927 | *uid = k; | |
4928 | return 0; | |
4929 | } | |
4930 | ||
4931 | int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
4932 | char **s = userdata; | |
4933 | const char *n; | |
4934 | int r; | |
4935 | ||
4936 | if (json_variant_is_null(variant)) { | |
4937 | *s = mfree(*s); | |
4938 | return 0; | |
4939 | } | |
4940 | ||
4941 | if (!json_variant_is_string(variant)) | |
4942 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); | |
4943 | ||
4944 | n = json_variant_string(variant); | |
7a8867ab | 4945 | if (!valid_user_group_name(n, FLAGS_SET(flags, JSON_RELAX) ? VALID_USER_RELAX : 0)) |
a42ef715 LP |
4946 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid user/group name.", strna(name)); |
4947 | ||
4948 | r = free_and_strdup(s, n); | |
4949 | if (r < 0) | |
4950 | return json_log(variant, flags, r, "Failed to allocate string: %m"); | |
4951 | ||
4952 | return 0; | |
4953 | } | |
4954 | ||
4955 | int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
4956 | sd_id128_t *uuid = userdata; | |
4957 | int r; | |
4958 | ||
4959 | if (json_variant_is_null(variant)) { | |
4960 | *uuid = SD_ID128_NULL; | |
4961 | return 0; | |
4962 | } | |
4963 | ||
4964 | if (!json_variant_is_string(variant)) | |
4965 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); | |
4966 | ||
4967 | r = sd_id128_from_string(json_variant_string(variant), uuid); | |
4968 | if (r < 0) | |
4969 | return json_log(variant, flags, r, "JSON field '%s' is not a valid UID.", strna(name)); | |
4970 | ||
4971 | return 0; | |
4972 | } | |
4973 | ||
4974 | int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
4975 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not allowed in this object.", strna(name)); | |
4976 | } | |
4977 | ||
03427720 LP |
4978 | int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { |
4979 | _cleanup_free_ void *buffer = NULL; | |
4980 | struct iovec *iov = ASSERT_PTR(userdata); | |
4981 | size_t sz; | |
4982 | int r; | |
4983 | ||
4984 | if (!json_variant_is_string(variant)) | |
4985 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); | |
4986 | ||
4987 | r = json_variant_unbase64(variant, &buffer, &sz); | |
4988 | if (r < 0) | |
4989 | return json_log(variant, flags, r, "JSON field '%s' is not valid Base64 data.", strna(name)); | |
4990 | ||
4991 | free_and_replace(iov->iov_base, buffer); | |
4992 | iov->iov_len = sz; | |
4993 | return 0; | |
4994 | } | |
4995 | ||
b7fc90a2 LP |
4996 | static int json_cmp_strings(const void *x, const void *y) { |
4997 | JsonVariant *const *a = x, *const *b = y; | |
4998 | ||
4999 | if (!json_variant_is_string(*a) || !json_variant_is_string(*b)) | |
5000 | return CMP(*a, *b); | |
5001 | ||
5002 | return strcmp(json_variant_string(*a), json_variant_string(*b)); | |
5003 | } | |
5004 | ||
5005 | int json_variant_sort(JsonVariant **v) { | |
5006 | _cleanup_free_ JsonVariant **a = NULL; | |
99b1145a | 5007 | _cleanup_(json_variant_unrefp) JsonVariant *n = NULL; |
a4669764 | 5008 | size_t m; |
b7fc90a2 LP |
5009 | int r; |
5010 | ||
5011 | assert(v); | |
5012 | ||
5013 | if (json_variant_is_sorted(*v)) | |
5014 | return 0; | |
5015 | ||
5016 | if (!json_variant_is_object(*v)) | |
5017 | return -EMEDIUMTYPE; | |
5018 | ||
5019 | /* Sorts they key/value pairs in an object variant */ | |
5020 | ||
5021 | m = json_variant_elements(*v); | |
5022 | a = new(JsonVariant*, m); | |
5023 | if (!a) | |
5024 | return -ENOMEM; | |
5025 | ||
a4669764 | 5026 | for (size_t i = 0; i < m; i++) |
b7fc90a2 LP |
5027 | a[i] = json_variant_by_index(*v, i); |
5028 | ||
5029 | qsort(a, m/2, sizeof(JsonVariant*)*2, json_cmp_strings); | |
5030 | ||
5031 | r = json_variant_new_object(&n, a, m); | |
5032 | if (r < 0) | |
5033 | return r; | |
94600eeb LP |
5034 | |
5035 | json_variant_propagate_sensitive(*v, n); | |
5036 | ||
b7fc90a2 LP |
5037 | if (!n->sorted) /* Check if this worked. This will fail if there are multiple identical keys used. */ |
5038 | return -ENOTUNIQ; | |
5039 | ||
99b1145a | 5040 | JSON_VARIANT_REPLACE(*v, TAKE_PTR(n)); |
b7fc90a2 LP |
5041 | |
5042 | return 1; | |
5043 | } | |
5044 | ||
5045 | int json_variant_normalize(JsonVariant **v) { | |
5046 | _cleanup_free_ JsonVariant **a = NULL; | |
3b6ce055 | 5047 | _cleanup_(json_variant_unrefp) JsonVariant *n = NULL; |
a4669764 | 5048 | size_t i, m; |
b7fc90a2 LP |
5049 | int r; |
5050 | ||
5051 | assert(v); | |
5052 | ||
5053 | if (json_variant_is_normalized(*v)) | |
5054 | return 0; | |
5055 | ||
5056 | if (!json_variant_is_object(*v) && !json_variant_is_array(*v)) | |
5057 | return -EMEDIUMTYPE; | |
5058 | ||
5059 | /* Sorts the key/value pairs in an object variant anywhere down the tree in the specified variant */ | |
5060 | ||
5061 | m = json_variant_elements(*v); | |
5062 | a = new(JsonVariant*, m); | |
5063 | if (!a) | |
5064 | return -ENOMEM; | |
5065 | ||
7e4be6a5 | 5066 | for (i = 0; i < m; ) { |
b7fc90a2 | 5067 | a[i] = json_variant_ref(json_variant_by_index(*v, i)); |
7e4be6a5 | 5068 | i++; |
b7fc90a2 | 5069 | |
7e4be6a5 | 5070 | r = json_variant_normalize(&a[i-1]); |
b7fc90a2 LP |
5071 | if (r < 0) |
5072 | goto finish; | |
5073 | } | |
5074 | ||
5075 | qsort(a, m/2, sizeof(JsonVariant*)*2, json_cmp_strings); | |
5076 | ||
5077 | if (json_variant_is_object(*v)) | |
5078 | r = json_variant_new_object(&n, a, m); | |
5079 | else { | |
5080 | assert(json_variant_is_array(*v)); | |
5081 | r = json_variant_new_array(&n, a, m); | |
5082 | } | |
5083 | if (r < 0) | |
5084 | goto finish; | |
94600eeb LP |
5085 | |
5086 | json_variant_propagate_sensitive(*v, n); | |
5087 | ||
b7fc90a2 LP |
5088 | if (!n->normalized) { /* Let's see if normalization worked. It will fail if there are multiple |
5089 | * identical keys used in the same object anywhere, or if there are floating | |
5090 | * point numbers used (see below) */ | |
5091 | r = -ENOTUNIQ; | |
5092 | goto finish; | |
5093 | } | |
5094 | ||
3b6ce055 | 5095 | JSON_VARIANT_REPLACE(*v, TAKE_PTR(n)); |
b7fc90a2 LP |
5096 | |
5097 | r = 1; | |
5098 | ||
5099 | finish: | |
a4669764 | 5100 | for (size_t j = 0; j < i; j++) |
b7fc90a2 LP |
5101 | json_variant_unref(a[j]); |
5102 | ||
5103 | return r; | |
5104 | } | |
5105 | ||
5106 | bool json_variant_is_normalized(JsonVariant *v) { | |
bac06497 ZJS |
5107 | /* For now, let's consider anything containing numbers not expressible as integers as non-normalized. |
5108 | * That's because we cannot sensibly compare them due to accuracy issues, nor even store them if they | |
5109 | * are too large. */ | |
b7fc90a2 LP |
5110 | if (json_variant_is_real(v) && !json_variant_is_integer(v) && !json_variant_is_unsigned(v)) |
5111 | return false; | |
5112 | ||
5113 | /* The concept only applies to variants that include other variants, i.e. objects and arrays. All | |
5114 | * others are normalized anyway. */ | |
5115 | if (!json_variant_is_object(v) && !json_variant_is_array(v)) | |
5116 | return true; | |
5117 | ||
5118 | /* Empty objects/arrays don't include any other variant, hence are always normalized too */ | |
5119 | if (json_variant_elements(v) == 0) | |
5120 | return true; | |
5121 | ||
5122 | return v->normalized; /* For everything else there's an explicit boolean we maintain */ | |
5123 | } | |
5124 | ||
5125 | bool json_variant_is_sorted(JsonVariant *v) { | |
5126 | ||
5127 | /* Returns true if all key/value pairs of an object are properly sorted. Note that this only applies | |
5128 | * to objects, not arrays. */ | |
5129 | ||
5130 | if (!json_variant_is_object(v)) | |
5131 | return true; | |
5132 | if (json_variant_elements(v) <= 1) | |
5133 | return true; | |
5134 | ||
5135 | return v->sorted; | |
5136 | } | |
5137 | ||
faca141c | 5138 | int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) { |
faca141c LP |
5139 | if (!json_variant_is_string(v)) |
5140 | return -EINVAL; | |
5141 | ||
bdd2036e | 5142 | return unbase64mem(json_variant_string(v), ret, ret_size); |
faca141c LP |
5143 | } |
5144 | ||
2d64d2b9 | 5145 | int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) { |
2d64d2b9 LP |
5146 | if (!json_variant_is_string(v)) |
5147 | return -EINVAL; | |
5148 | ||
bdd2036e | 5149 | return unhexmem(json_variant_string(v), ret, ret_size); |
2d64d2b9 LP |
5150 | } |
5151 | ||
cd0b6c53 | 5152 | static const char* const json_variant_type_table[_JSON_VARIANT_TYPE_MAX] = { |
9674b089 ZJS |
5153 | [JSON_VARIANT_STRING] = "string", |
5154 | [JSON_VARIANT_INTEGER] = "integer", | |
cd0b6c53 | 5155 | [JSON_VARIANT_UNSIGNED] = "unsigned", |
9674b089 ZJS |
5156 | [JSON_VARIANT_REAL] = "real", |
5157 | [JSON_VARIANT_NUMBER] = "number", | |
5158 | [JSON_VARIANT_BOOLEAN] = "boolean", | |
5159 | [JSON_VARIANT_ARRAY] = "array", | |
5160 | [JSON_VARIANT_OBJECT] = "object", | |
5161 | [JSON_VARIANT_NULL] = "null", | |
cd0b6c53 LP |
5162 | }; |
5163 | ||
5164 | DEFINE_STRING_TABLE_LOOKUP(json_variant_type, JsonVariantType); |