]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/varlink-idl.c
tree-wide: s/life-cycle/lifecycle/g
[thirdparty/systemd.git] / src / shared / varlink-idl.c
CommitLineData
e50b2a93
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "memstream-util.h"
4#include "strv.h"
5#include "terminal-util.h"
6#include "varlink-idl.h"
7#include "set.h"
8
69d17e23
FS
9#define DEPTH_MAX 64U
10
e50b2a93
LP
11enum {
12 COLOR_SYMBOL_TYPE, /* interface, method, type, error */
13 COLOR_FIELD_TYPE, /* string, bool, … */
14 COLOR_IDENTIFIER,
15 COLOR_MARKS, /* [], ->, ?, … */
16 COLOR_RESET,
17 _COLOR_MAX,
18};
19
20static int varlink_idl_format_all_fields(FILE *f, const VarlinkSymbol *symbol, VarlinkFieldDirection direction, const char *indent, const char *const colors[static _COLOR_MAX]);
21
22static int varlink_idl_format_enum_values(
23 FILE *f,
24 const VarlinkSymbol *symbol,
25 const char *indent,
26 const char *const colors[static _COLOR_MAX]) {
27
28 bool first = true;
29
30 assert(f);
31 assert(symbol);
32 assert(symbol->symbol_type == VARLINK_ENUM_TYPE);
33
34 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
35
36 if (first) {
37 first = false;
38 fputs("(\n", f);
39 } else
40 fputs(",\n", f);
41
42 fputs(strempty(indent), f);
43 fputs("\t", f);
44 fputs(colors[COLOR_IDENTIFIER], f);
45 fputs(field->name, f);
46 fputs(colors[COLOR_RESET], f);
47 }
48
49 if (first)
50 fputs("()", f);
51 else {
52 fputs("\n", f);
53 fputs(strempty(indent), f);
54 fputs(")", f);
55 }
56
57 return 0;
58}
59
60static int varlink_idl_format_field(
61 FILE *f,
62 const VarlinkField *field,
63 const char *indent,
64 const char *const colors[static _COLOR_MAX]) {
65
66 assert(f);
67 assert(field);
68
69 fputs(strempty(indent), f);
70 fputs(colors[COLOR_IDENTIFIER], f);
71 fputs(field->name, f);
72 fputs(colors[COLOR_RESET], f);
73 fputs(": ", f);
74
75 if (FLAGS_SET(field->field_flags, VARLINK_NULLABLE)) {
76 fputs(colors[COLOR_MARKS], f);
77 fputs("?", f);
78 fputs(colors[COLOR_RESET], f);
79 }
80
81 switch (field->field_flags & (VARLINK_MAP|VARLINK_ARRAY)) {
82
83 case VARLINK_MAP:
84 fputs(colors[COLOR_MARKS], f);
85 fputs("[", f);
86 fputs(colors[COLOR_FIELD_TYPE], f);
87 fputs("string", f);
88 fputs(colors[COLOR_MARKS], f);
89 fputs("]", f);
90 fputs(colors[COLOR_RESET], f);
91 break;
92
93 case VARLINK_ARRAY:
94 fputs(colors[COLOR_MARKS], f);
95 fputs("[]", f);
96 fputs(colors[COLOR_RESET], f);
97 break;
98
99 case 0:
100 break;
101
102 default:
103 assert_not_reached();
104 }
105
106 switch (field->field_type) {
107
108 case VARLINK_BOOL:
109 fputs(colors[COLOR_FIELD_TYPE], f);
110 fputs("bool", f);
111 fputs(colors[COLOR_RESET], f);
112 break;
113
114 case VARLINK_INT:
115 fputs(colors[COLOR_FIELD_TYPE], f);
116 fputs("int", f);
117 fputs(colors[COLOR_RESET], f);
118 break;
119
120 case VARLINK_FLOAT:
121 fputs(colors[COLOR_FIELD_TYPE], f);
122 fputs("float", f);
123 fputs(colors[COLOR_RESET], f);
124 break;
125
126 case VARLINK_STRING:
127 fputs(colors[COLOR_FIELD_TYPE], f);
128 fputs("string", f);
129 fputs(colors[COLOR_RESET], f);
130 break;
131
132 case VARLINK_OBJECT:
133 fputs(colors[COLOR_FIELD_TYPE], f);
134 fputs("object", f);
135 fputs(colors[COLOR_RESET], f);
136 break;
137
138 case VARLINK_NAMED_TYPE:
139 fputs(colors[COLOR_IDENTIFIER], f);
140 fputs(ASSERT_PTR(field->named_type), f);
141 fputs(colors[COLOR_RESET], f);
142 break;
143
144 case VARLINK_STRUCT:
145 return varlink_idl_format_all_fields(f, ASSERT_PTR(field->symbol), VARLINK_REGULAR, indent, colors);
146
147 case VARLINK_ENUM:
148 return varlink_idl_format_enum_values(f, ASSERT_PTR(field->symbol), indent, colors);
149
150 default:
151 assert_not_reached();
152 }
153
154 return 0;
155}
156
157static int varlink_idl_format_all_fields(
158 FILE *f,
159 const VarlinkSymbol *symbol,
160 VarlinkFieldDirection filter_direction,
161 const char *indent,
162 const char *const colors[static _COLOR_MAX]) {
163
164 _cleanup_free_ char *indent2 = NULL;
165 bool first = true;
166 int r;
167
168 assert(f);
169 assert(symbol);
170 assert(IN_SET(symbol->symbol_type, VARLINK_STRUCT_TYPE, VARLINK_METHOD, VARLINK_ERROR));
171
172 indent2 = strjoin(strempty(indent), "\t");
173 if (!indent2)
174 return -ENOMEM;
175
176 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
177
178 if (field->field_direction != filter_direction)
179 continue;
180
181 if (first) {
182 first = false;
183 fputs("(\n", f);
184 } else
185 fputs(",\n", f);
186
187 r = varlink_idl_format_field(f, field, indent2, colors);
188 if (r < 0)
189 return r;
190 }
191
192 if (first)
193 fputs("()", f);
194 else {
195 fputs("\n", f);
196 fputs(strempty(indent), f);
197 fputs(")", f);
198 }
199
200 return 0;
201}
202
203static int varlink_idl_format_symbol(
204 FILE *f,
205 const VarlinkSymbol *symbol,
206 const char *const colors[static _COLOR_MAX]) {
207 int r;
208
209 assert(f);
210 assert(symbol);
211
212 switch (symbol->symbol_type) {
213
214 case VARLINK_ENUM_TYPE:
215 fputs(colors[COLOR_SYMBOL_TYPE], f);
216 fputs("type ", f);
217 fputs(colors[COLOR_IDENTIFIER], f);
218 fputs(symbol->name, f);
219 fputs(colors[COLOR_RESET], f);
220
221 r = varlink_idl_format_enum_values(f, symbol, /* indent= */ NULL, colors);
222 break;
223
224 case VARLINK_STRUCT_TYPE:
225 fputs(colors[COLOR_SYMBOL_TYPE], f);
226 fputs("type ", f);
227 fputs(colors[COLOR_IDENTIFIER], f);
228 fputs(symbol->name, f);
229 fputs(colors[COLOR_RESET], f);
230
231 r = varlink_idl_format_all_fields(f, symbol, VARLINK_REGULAR, /* indent= */ NULL, colors);
232 break;
233
234 case VARLINK_METHOD:
235 fputs(colors[COLOR_SYMBOL_TYPE], f);
236 fputs("method ", f);
237 fputs(colors[COLOR_IDENTIFIER], f);
238 fputs(symbol->name, f);
239 fputs(colors[COLOR_RESET], f);
240
241 r = varlink_idl_format_all_fields(f, symbol, VARLINK_INPUT, /* indent= */ NULL, colors);
242 if (r < 0)
243 return r;
244
245 fputs(colors[COLOR_MARKS], f);
246 fputs(" -> ", f);
247 fputs(colors[COLOR_RESET], f);
248
249 r = varlink_idl_format_all_fields(f, symbol, VARLINK_OUTPUT, /* indent= */ NULL, colors);
250 break;
251
252 case VARLINK_ERROR:
253 fputs(colors[COLOR_SYMBOL_TYPE], f);
254 fputs("error ", f);
255 fputs(colors[COLOR_IDENTIFIER], f);
256 fputs(symbol->name, f);
257 fputs(colors[COLOR_RESET], f);
258
259 r = varlink_idl_format_all_fields(f, symbol, VARLINK_REGULAR, /* indent= */ NULL, colors);
260 break;
261
262 default:
263 assert_not_reached();
264 }
265 if (r < 0)
266 return r;
267
268 fputs("\n", f);
269 return 0;
270}
271
272static int varlink_idl_format_all_symbols(
273 FILE *f,
274 const VarlinkInterface *interface,
275 VarlinkSymbolType filter_type,
276 const char *const colors[static _COLOR_MAX]) {
277
278 int r;
279
280 assert(f);
281 assert(interface);
282
283 for (const VarlinkSymbol *const*symbol = interface->symbols; *symbol; symbol++) {
284
285 if ((*symbol)->symbol_type != filter_type)
286 continue;
287
288 fputs("\n", f);
289
290 r = varlink_idl_format_symbol(f, *symbol, colors);
291 if (r < 0)
292 return r;
293 }
294
295 return 0;
296}
297
298int varlink_idl_dump(FILE *f, int use_colors, const VarlinkInterface *interface) {
299 static const char* const color_table[_COLOR_MAX] = {
300 [COLOR_SYMBOL_TYPE] = ANSI_HIGHLIGHT_GREEN,
301 [COLOR_FIELD_TYPE] = ANSI_HIGHLIGHT_BLUE,
302 [COLOR_IDENTIFIER] = ANSI_NORMAL,
303 [COLOR_MARKS] = ANSI_HIGHLIGHT_MAGENTA,
304 [COLOR_RESET] = ANSI_NORMAL,
305 };
306
307 static const char* const color_off[_COLOR_MAX] = {
308 "", "", "", "", "",
309 };
310
311 int r;
312
313 assert(interface);
314
315 if (!f)
316 f = stdout;
317
318 if (use_colors < 0)
319 use_colors = colors_enabled();
320
321 const char *const *colors = use_colors ? color_table : color_off;
322
323 fputs(colors[COLOR_SYMBOL_TYPE], f);
324 fputs("interface ", f);
325 fputs(colors[COLOR_IDENTIFIER], f);
326 fputs(ASSERT_PTR(interface->name), f);
327 fputs(colors[COLOR_RESET], f);
328 fputs("\n", f);
329
330 for (VarlinkSymbolType t = 0; t < _VARLINK_SYMBOL_TYPE_MAX; t++) {
331 r = varlink_idl_format_all_symbols(f, interface, t, colors);
332 if (r < 0)
333 return r;
334 }
335
336 return 0;
337}
338
339int varlink_idl_format(const VarlinkInterface *interface, char **ret) {
340 _cleanup_(memstream_done) MemStream memstream = {};
341 int r;
342
343 if (!memstream_init(&memstream))
344 return -errno;
345
346 r = varlink_idl_dump(memstream.f, /* use_colors= */ false, interface);
347 if (r < 0)
348 return r;
349
350 return memstream_finalize(&memstream, ret, NULL);
351}
352
353static VarlinkSymbol *varlink_symbol_free(VarlinkSymbol *symbol) {
354 if (!symbol)
355 return NULL;
356
357 /* See comment in varlink_interface_free() regarding the casting away of `const` */
358
359 free((char*) symbol->name);
360
361 for (size_t i = 0; symbol->fields[i].field_type != _VARLINK_FIELD_TYPE_END_MARKER; i++) {
362 VarlinkField *field = symbol->fields + i;
363
364 free((void*) field->name);
365 free((void*) field->named_type);
366
367 /* The symbol pointer might either point to a named symbol, in which case that symbol is
368 * owned by the interface, or by an anomyous symbol, in which case it is owned by us, and we
369 * need to free it */
370 if (field->symbol && field->field_type != VARLINK_NAMED_TYPE)
371 varlink_symbol_free((VarlinkSymbol*) field->symbol);
372 }
373
374 return mfree(symbol);
375}
376
377VarlinkInterface* varlink_interface_free(VarlinkInterface *interface) {
378 if (!interface)
379 return NULL;
380
381 /* So here's the thing: in most cases we want that users of this define their interface descriptions
55e40b0b 382 * in C code, and hence the definitions are constant and immutable during the lifecycle of the
e50b2a93
LP
383 * system. Because of that we define all structs with const* pointers. It makes it very nice and
384 * straight-forward to populate these structs with literal C strings. However, in some not so common
385 * cases we also want to allocate these structures dynamically on the heap, when parsing interface
386 * descriptions. But given this should be the exceptional and not the common case, we decided to
387 * simple cast away the 'const' where needed, even if it is ugly. */
388
389 free((char*) interface->name);
390
391 for (size_t i = 0; interface->symbols[i]; i++)
392 varlink_symbol_free((VarlinkSymbol*) interface->symbols[i]);
393
394 return mfree(interface);
395}
396
397DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkSymbol*, varlink_symbol_free);
398
399static int varlink_interface_realloc(VarlinkInterface **interface, size_t n_symbols) {
400 VarlinkInterface *n;
401
402 assert(interface);
403
404 n_symbols ++; /* Space for trailing NULL end marker symbol */
405
406 /* Overflow check */
407 if (n_symbols > (SIZE_MAX - offsetof(VarlinkInterface, symbols)) / sizeof(VarlinkSymbol*))
408 return -ENOMEM;
409
410 n = realloc0(*interface, offsetof(VarlinkInterface, symbols) + sizeof(VarlinkSymbol*) * n_symbols);
411 if (!n)
412 return -ENOMEM;
413
414 *interface = n;
415 return 0;
416}
417
418static int varlink_symbol_realloc(VarlinkSymbol **symbol, size_t n_fields) {
419 VarlinkSymbol *n;
420
421 assert(symbol);
422
423 n_fields ++; /* Space for trailing end marker field */
424
425 /* Overflow check */
426 if (n_fields > (SIZE_MAX - offsetof(VarlinkSymbol, fields)) / sizeof(VarlinkField))
427 return -ENOMEM;
428
429 n = realloc0(*symbol, offsetof(VarlinkSymbol, fields) + sizeof(VarlinkField) * n_fields);
430 if (!n)
431 return -ENOMEM;
432
433 *symbol = n;
434 return 0;
435}
436
437#define VALID_CHARS_IDENTIFIER ALPHANUMERICAL "_"
438#define VALID_CHARS_RESERVED LOWERCASE_LETTERS
439#define VALID_CHARS_INTERFACE_NAME ALPHANUMERICAL ".-"
440
441static void advance_line_column(const char *p, size_t n, unsigned *line, unsigned *column) {
442
443 assert(p);
444 assert(line);
445 assert(column);
446
447 for (; n > 0; p++, n--) {
448
449 if (*p == '\n') {
450 (*line)++;
451 *column = 1;
452 } else
453 (*column)++;
454 }
455}
456
457static size_t token_match(
458 const char *p,
459 const char *allowed_delimiters,
460 const char *allowed_chars) {
461
462 /* Checks if the string p begins either with one of the token characters in allowed_delimiters or
463 * with a string consisting of allowed_chars. */
464
465 assert(p);
466
467 if (allowed_delimiters && strchr(allowed_delimiters, *p))
468 return 1;
469
470 if (!allowed_chars)
471 return 0;
472
473 return strspn(p, allowed_chars);
474}
475
476static int varlink_idl_subparse_token(
477 const char **p,
478 unsigned *line,
479 unsigned *column,
480 const char *allowed_delimiters,
481 const char *allowed_chars,
482 char **ret_token) {
483
484 _cleanup_free_ char *t = NULL;
485 size_t l;
486
487 assert(p);
488 assert(*p);
489 assert(line);
490 assert(column);
491 assert(ret_token);
492
493 if (**p == '\0') { /* eof */
494 *ret_token = NULL;
495 return 0;
496 }
497
498 l = token_match(*p, allowed_delimiters, allowed_chars);
499
500 /* No token of the permitted character set found? Then let's try to skip over whitespace and try again */
501 if (l == 0) {
502 size_t ll;
503
504 ll = strspn(*p, WHITESPACE);
505 advance_line_column(*p, ll, line, column);
506 *p += ll;
507
508 if (**p == '\0') { /* eof */
509 *ret_token = NULL;
510 return 0;
511 }
512
513 l = token_match(*p, allowed_delimiters, allowed_chars);
514 if (l == 0)
515 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Couldn't find token of allowed chars '%s' or allowed delimiters '%s'.", strempty(allowed_chars), strempty(allowed_delimiters));
516 }
517
518 t = strndup(*p, l);
519 if (!t)
520 return -ENOMEM;
521
522 advance_line_column(*p, l, line, column);
523 *p += l;
524
525 *ret_token = TAKE_PTR(t);
526 return 1;
527}
528
529static int varlink_idl_subparse_comment(
530 const char **p,
531 unsigned *line,
532 unsigned *column) {
533
534 size_t l;
535
536 assert(p);
537 assert(*p);
538 assert(line);
539 assert(column);
540
541 l = strcspn(*p, NEWLINE);
542 advance_line_column(*p, l + 1, line, column);
543 *p += l;
544
545 return 1;
546}
547
548static int varlink_idl_subparse_whitespace(
549 const char **p,
550 unsigned *line,
551 unsigned *column) {
552
553 size_t l;
554
555 assert(p);
556 assert(*p);
557 assert(line);
558 assert(column);
559
560 l = strspn(*p, WHITESPACE);
561 advance_line_column(*p, l, line, column);
562 *p += l;
563
564 return 1;
565}
566
69d17e23 567static int varlink_idl_subparse_struct_or_enum(const char **p, unsigned *line, unsigned *column, VarlinkSymbol **symbol, size_t *n_fields, VarlinkFieldDirection direction, unsigned depth);
e50b2a93
LP
568
569static int varlink_idl_subparse_field_type(
570 const char **p,
571 unsigned *line,
572 unsigned *column,
69d17e23
FS
573 VarlinkField *field,
574 unsigned depth) {
e50b2a93
LP
575
576 size_t l;
577 int r;
578
579 assert(p);
580 assert(*p);
581 assert(line);
582 assert(field);
583
584 r = varlink_idl_subparse_whitespace(p, line, column);
585 if (r < 0)
586 return r;
587
588 if (startswith(*p, "?")) {
589 field->field_flags |= VARLINK_NULLABLE;
590 l = 1;
591 } else {
592 field->field_flags &= ~VARLINK_NULLABLE;
593 l = 0;
594 }
595
596 advance_line_column(*p, l, line, column);
597 *p += l;
598
599 if (startswith(*p, "[]")) {
600 l = 2;
601 field->field_flags = (field->field_flags & ~VARLINK_MAP) | VARLINK_ARRAY;
602 } else if (startswith(*p, "[string]")) {
603 l = 8;
604 field->field_flags = (field->field_flags & ~VARLINK_ARRAY) | VARLINK_MAP;
605 } else {
606 l = 0;
607 field->field_flags = field->field_flags & ~(VARLINK_MAP | VARLINK_ARRAY);
608 }
609
610 advance_line_column(*p, l, line, column);
611 *p += l;
612
613 if (startswith(*p, "bool")) {
614 l = 4;
615 field->field_type = VARLINK_BOOL;
616 } else if (startswith(*p, "int")) {
617 l = 3;
618 field->field_type = VARLINK_INT;
619 } else if (startswith(*p, "float")) {
620 l = 5;
621 field->field_type = VARLINK_FLOAT;
622 } else if (startswith(*p, "string")) {
623 l = 6;
624 field->field_type = VARLINK_STRING;
625 } else if (startswith(*p, "object")) {
626 l = 6;
627 field->field_type = VARLINK_OBJECT;
628 } else if (**p == '(') {
629 _cleanup_(varlink_symbol_freep) VarlinkSymbol *symbol = NULL;
630 size_t n_fields = 0;
631
632 r = varlink_symbol_realloc(&symbol, n_fields);
633 if (r < 0)
634 return r;
635
636 symbol->symbol_type = _VARLINK_SYMBOL_TYPE_INVALID;
637
638 r = varlink_idl_subparse_struct_or_enum(
639 p,
640 line,
641 column,
642 &symbol,
643 &n_fields,
69d17e23
FS
644 VARLINK_REGULAR,
645 depth + 1);
e50b2a93
LP
646 if (r < 0)
647 return r;
648
649 if (symbol->symbol_type == VARLINK_STRUCT_TYPE)
650 field->field_type = VARLINK_STRUCT;
651 else {
652 assert(symbol->symbol_type == VARLINK_ENUM_TYPE);
653 field->field_type = VARLINK_ENUM;
654 }
655
656 field->symbol = TAKE_PTR(symbol);
657 l = 0;
658 } else {
659 _cleanup_free_ char *token = NULL;
660
661 r = varlink_idl_subparse_token(p, line, column, /* valid_tokens= */ NULL, VALID_CHARS_IDENTIFIER, &token);
662 if (r < 0)
663 return r;
664 if (!token)
efe511e9 665 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e50b2a93
LP
666
667 field->named_type = TAKE_PTR(token);
668 field->field_type = VARLINK_NAMED_TYPE;
669 l = 0;
670 }
671
672 advance_line_column(*p, l, line, column);
673 *p += l;
674
675 return 0;
676}
677
678static int varlink_idl_subparse_struct_or_enum(
679 const char **p,
680 unsigned *line,
681 unsigned *column,
682 VarlinkSymbol **symbol,
683 size_t *n_fields,
69d17e23
FS
684 VarlinkFieldDirection direction,
685 unsigned depth) {
e50b2a93
LP
686
687 enum {
688 STATE_OPEN,
689 STATE_NAME,
690 STATE_COLON,
691 STATE_COMMA,
692 STATE_DONE,
693 } state = STATE_OPEN;
694 _cleanup_free_ char *field_name = NULL;
695 const char *allowed_delimiters = "(", *allowed_chars = NULL;
696 int r;
697
698 assert(p);
699 assert(*p);
700 assert(line);
701 assert(column);
702 assert(symbol);
703 assert(*symbol);
704 assert(n_fields);
705
69d17e23
FS
706 if (depth > DEPTH_MAX)
707 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Maximum nesting depth reached (%u).", *line, *column, DEPTH_MAX);
708
e50b2a93
LP
709 while (state != STATE_DONE) {
710 _cleanup_free_ char *token = NULL;
711
712 r = varlink_idl_subparse_token(
713 p,
714 line,
715 column,
716 allowed_delimiters,
717 allowed_chars,
718 &token);
719 if (r < 0)
720 return r;
721
722 switch (state) {
723
724 case STATE_OPEN:
725 if (!token)
efe511e9 726 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e50b2a93 727 if (!streq(token, "("))
efe511e9 728 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
e50b2a93
LP
729
730 state = STATE_NAME;
731 allowed_delimiters = ")";
732 allowed_chars = VALID_CHARS_IDENTIFIER;
733 break;
734
735 case STATE_NAME:
736 assert(!field_name);
737
738 if (!token)
efe511e9 739 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e97f2dfd 740 if (streq(token, ")"))
e50b2a93
LP
741 state = STATE_DONE;
742 else {
743 field_name = TAKE_PTR(token);
744 state = STATE_COLON;
745 allowed_delimiters = ":,)";
746 allowed_chars = NULL;
747 }
748
749 break;
750
751 case STATE_COLON:
752 assert(field_name);
753
754 if (!token)
efe511e9 755 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e50b2a93
LP
756
757 if (streq(token, ":")) {
758 VarlinkField *field;
759
760 if ((*symbol)->symbol_type < 0)
761 (*symbol)->symbol_type = VARLINK_STRUCT_TYPE;
762 if ((*symbol)->symbol_type == VARLINK_ENUM_TYPE)
efe511e9 763 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Enum with struct fields, refusing.", *line, *column);
e50b2a93
LP
764
765 r = varlink_symbol_realloc(symbol, *n_fields + 1);
766 if (r < 0)
767 return r;
768
769 field = (*symbol)->fields + (*n_fields)++;
770 *field = (VarlinkField) {
771 .name = TAKE_PTR(field_name),
772 .field_type = _VARLINK_FIELD_TYPE_INVALID,
773 .field_direction = direction,
774 };
775
69d17e23 776 r = varlink_idl_subparse_field_type(p, line, column, field, depth);
e50b2a93
LP
777 if (r < 0)
778 return r;
779
780 state = STATE_COMMA;
781 allowed_delimiters = ",)";
782 allowed_chars = NULL;
783
784 } else if (STR_IN_SET(token, ",", ")")) {
785 VarlinkField *field;
786
787 if ((*symbol)->symbol_type < 0)
788 (*symbol)->symbol_type = VARLINK_ENUM_TYPE;
789 if ((*symbol)->symbol_type != VARLINK_ENUM_TYPE)
efe511e9 790 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Struct with enum fields, refusing.", *line, *column);
e50b2a93
LP
791
792 r = varlink_symbol_realloc(symbol, *n_fields + 1);
793 if (r < 0)
794 return r;
795
796 field = (*symbol)->fields + (*n_fields)++;
797 *field = (VarlinkField) {
798 .name = TAKE_PTR(field_name),
799 .field_type = VARLINK_ENUM_VALUE,
800 };
801
802 if (streq(token, ",")) {
803 state = STATE_NAME;
804 allowed_delimiters = NULL;
805 allowed_chars = VALID_CHARS_IDENTIFIER;
806 } else {
807 assert(streq(token, ")"));
808 state = STATE_DONE;
809 }
810 } else
efe511e9 811 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
e50b2a93
LP
812
813 break;
814
815 case STATE_COMMA:
816 assert(!field_name);
817
818 if (!token)
efe511e9 819 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e50b2a93
LP
820 if (streq(token, ",")) {
821 state = STATE_NAME;
822 allowed_delimiters = NULL;
823 allowed_chars = VALID_CHARS_IDENTIFIER;
824 } else if (streq(token, ")"))
825 state = STATE_DONE;
826 else
efe511e9 827 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
e50b2a93
LP
828 break;
829
830 default:
831 assert_not_reached();
832 }
833 }
834
6fa09278
LP
835 /* If we don't know the type of the symbol by now it was an empty () which doesn't allow us to
836 * determine if we look at an enum or a struct */
837 if ((*symbol)->symbol_type < 0)
838 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Ambiguous empty () enum/struct is not permitted.", *line, *column);
839
e50b2a93
LP
840 return 0;
841}
842
843static int varlink_idl_resolve_symbol_types(VarlinkInterface *interface, VarlinkSymbol *symbol) {
844 assert(interface);
845 assert(symbol);
846
847 for (VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
848 const VarlinkSymbol *found;
849
850 if (field->field_type != VARLINK_NAMED_TYPE)
851 continue;
852
853 if (field->symbol) /* Already resolved */
854 continue;
855
856 if (!field->named_type)
857 return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH), "Named type field lacking a type name.");
858
859 found = varlink_idl_find_symbol(interface, _VARLINK_SYMBOL_TYPE_INVALID, field->named_type);
860 if (!found)
861 return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH), "Failed to find type '%s'.", field->named_type);
862
863 if (!IN_SET(found->symbol_type, VARLINK_STRUCT_TYPE, VARLINK_ENUM_TYPE))
864 return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH), "Symbol '%s' is referenced as type but is not a type.", field->named_type);
865
866 field->symbol = found;
867 }
868
869 return 0;
870}
871
872static int varlink_idl_resolve_types(VarlinkInterface *interface) {
873 int r;
874
875 assert(interface);
876
877 for (VarlinkSymbol **symbol = (VarlinkSymbol**) interface->symbols; *symbol; symbol++) {
878 r = varlink_idl_resolve_symbol_types(interface, *symbol);
879 if (r < 0)
880 return r;
881 }
882
883 return 0;
884}
885
886int varlink_idl_parse(
887 const char *text,
888 unsigned *line,
889 unsigned *column,
890 VarlinkInterface **ret) {
891
892 _cleanup_(varlink_interface_freep) VarlinkInterface *interface = NULL;
893 _cleanup_(varlink_symbol_freep) VarlinkSymbol *symbol = NULL;
894 enum {
895 STATE_PRE_INTERFACE,
896 STATE_INTERFACE,
897 STATE_PRE_SYMBOL,
898 STATE_METHOD,
899 STATE_METHOD_NAME,
900 STATE_METHOD_ARROW,
901 STATE_TYPE,
902 STATE_TYPE_NAME,
903 STATE_ERROR,
904 STATE_ERROR_NAME,
905 STATE_DONE,
906 } state = STATE_PRE_INTERFACE;
907 const char *allowed_delimiters = "#", *allowed_chars = VALID_CHARS_RESERVED;
908 size_t n_symbols = 0, n_fields = 1;
909 unsigned _line = 0, _column = 1;
910 const char **p = &text;
911 int r;
912
913 if (!line)
914 line = &_line;
915 if (!column)
916 column = &_column;
917
918 while (state != STATE_DONE) {
919 _cleanup_free_ char *token = NULL;
920
921 r = varlink_idl_subparse_token(
922 p,
923 line,
924 column,
925 allowed_delimiters,
926 allowed_chars,
927 &token);
928 if (r < 0)
929 return r;
930
931 switch (state) {
932
933 case STATE_PRE_INTERFACE:
934 if (!token)
efe511e9 935 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e50b2a93
LP
936 if (streq(token, "#")) {
937 r = varlink_idl_subparse_comment(&text, line, column);
938 if (r < 0)
939 return r;
940 } else if (streq(token, "interface")) {
941 state = STATE_INTERFACE;
942 allowed_delimiters = NULL;
943 allowed_chars = VALID_CHARS_INTERFACE_NAME;
944 } else
efe511e9 945 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
e50b2a93
LP
946 break;
947
948 case STATE_INTERFACE:
949 assert(!interface);
950 assert(n_symbols == 0);
951
952 if (!token)
efe511e9 953 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
e50b2a93
LP
954
955 r = varlink_interface_realloc(&interface, n_symbols);
956 if (r < 0)
957 return r;
958
959 interface->name = TAKE_PTR(token);
960 state = STATE_PRE_SYMBOL;
961 allowed_delimiters = "#";
962 allowed_chars = VALID_CHARS_RESERVED;
963 break;
964
965 case STATE_PRE_SYMBOL:
966 if (!token) {
967 state = STATE_DONE;
968 break;
969 }
970
971 if (streq(token, "#")) {
972 r = varlink_idl_subparse_comment(&text, line, column);
973 if (r < 0)
974 return r;
975 } else if (streq(token, "method")) {
976 state = STATE_METHOD;
977 allowed_chars = VALID_CHARS_IDENTIFIER;
978 } else if (streq(token, "type")) {
979 state = STATE_TYPE;
980 allowed_chars = VALID_CHARS_IDENTIFIER;
981 } else if (streq(token, "error")) {
982 state = STATE_ERROR;
983 allowed_chars = VALID_CHARS_IDENTIFIER;
984 } else
efe511e9 985 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
e50b2a93
LP
986
987 break;
988
989 case STATE_METHOD:
990 assert(!symbol);
991 n_fields = 0;
992
779e7b44
FS
993 if (!token)
994 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
995
e50b2a93
LP
996 r = varlink_symbol_realloc(&symbol, n_fields);
997 if (r < 0)
998 return r;
999
1000 symbol->symbol_type = VARLINK_METHOD;
1001 symbol->name = TAKE_PTR(token);
1002
69d17e23 1003 r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_INPUT, 0);
e50b2a93
LP
1004 if (r < 0)
1005 return r;
1006
1007 state = STATE_METHOD_ARROW;
1008 allowed_chars = "->";
1009 break;
1010
1011 case STATE_METHOD_ARROW:
1012 assert(symbol);
1013
779e7b44
FS
1014 if (!token)
1015 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
1016
e50b2a93 1017 if (!streq(token, "->"))
efe511e9 1018 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
e50b2a93 1019
69d17e23 1020 r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_OUTPUT, 0);
e50b2a93
LP
1021 if (r < 0)
1022 return r;
1023
1024 r = varlink_interface_realloc(&interface, n_symbols + 1);
1025 if (r < 0)
1026 return r;
1027
1028 interface->symbols[n_symbols++] = TAKE_PTR(symbol);
1029
1030 state = STATE_PRE_SYMBOL;
1031 allowed_chars = VALID_CHARS_RESERVED "#";
1032 break;
1033
1034 case STATE_TYPE:
1035 assert(!symbol);
1036 n_fields = 0;
1037
779e7b44
FS
1038 if (!token)
1039 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
1040
e50b2a93
LP
1041 r = varlink_symbol_realloc(&symbol, n_fields);
1042 if (r < 0)
1043 return r;
1044
939630ae 1045 symbol->symbol_type = _VARLINK_SYMBOL_TYPE_INVALID; /* don't know yet if enum or struct, will be field in by varlink_idl_subparse_struct_or_enum() */
e50b2a93
LP
1046 symbol->name = TAKE_PTR(token);
1047
69d17e23 1048 r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_REGULAR, 0);
e50b2a93
LP
1049 if (r < 0)
1050 return r;
1051
1052 r = varlink_interface_realloc(&interface, n_symbols + 1);
1053 if (r < 0)
1054 return r;
1055
1056 interface->symbols[n_symbols++] = TAKE_PTR(symbol);
1057
1058 state = STATE_PRE_SYMBOL;
1059 allowed_chars = VALID_CHARS_RESERVED "#";
1060 break;
1061
1062 case STATE_ERROR:
1063 assert(!symbol);
1064 n_fields = 0;
1065
779e7b44
FS
1066 if (!token)
1067 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
1068
e50b2a93
LP
1069 r = varlink_symbol_realloc(&symbol, n_fields);
1070 if (r < 0)
1071 return r;
1072
1073 symbol->symbol_type = VARLINK_ERROR;
1074 symbol->name = TAKE_PTR(token);
1075
69d17e23 1076 r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_REGULAR, 0);
e50b2a93
LP
1077 if (r < 0)
1078 return r;
1079
1080 r = varlink_interface_realloc(&interface, n_symbols + 1);
1081 if (r < 0)
1082 return r;
1083
1084 interface->symbols[n_symbols++] = TAKE_PTR(symbol);
1085
1086 state = STATE_PRE_SYMBOL;
1087 allowed_chars = VALID_CHARS_RESERVED "#";
1088 break;
1089
1090 default:
1091 assert_not_reached();
1092 }
1093 }
1094
1095 r = varlink_idl_resolve_types(interface);
1096 if (r < 0)
1097 return r;
1098
1099 *ret = TAKE_PTR(interface);
1100 return 0;
1101}
1102
1103bool varlink_idl_field_name_is_valid(const char *name) {
1104 if (isempty(name))
1105 return false;
1106
1107 /* Field names may start with lower or uppercase char, but no numerals or underscore */
1108 if (!strchr(LETTERS, name[0]))
1109 return false;
1110
1111 /* Otherwise fields may be alphanumerical or underscore, but no two underscore may immediately follow
1112 * each other or be trailing */
1113 bool underscore = false;
1114 for (const char *c = name + 1; *c; c++) {
1115 if (*c == '_') {
1116 if (underscore)
1117 return false;
1118
1119 underscore = true;
1120 continue;
1121 }
1122
1123 if (!strchr(ALPHANUMERICAL, *c))
1124 return false;
1125
1126 underscore = false;
1127 }
1128
1129 if (underscore)
1130 return false;
1131
1132 return true;
1133}
1134
1135bool varlink_idl_symbol_name_is_valid(const char *name) {
1136 if (isempty(name))
1137 return false;
1138
1139 /* We might want to reference VARLINK_STRUCT_TYPE and VARLINK_ENUM_TYPE symbols where we also
1140 * reference native types, hence make sure the native type names are refused as symbol names. */
1141 if (STR_IN_SET(name, "bool", "int", "float", "string", "object"))
1142 return false;
1143
1144 /* Symbols must be named with an uppercase letter as first character */
1145 if (!strchr(UPPERCASE_LETTERS, name[0]))
1146 return false;
1147
1148 for (const char *c = name + 1; *c; c++)
1149 if (!strchr(ALPHANUMERICAL, *c))
1150 return false;
1151
1152 return true;
1153}
1154
1155bool varlink_idl_interface_name_is_valid(const char *name) {
1156 if (isempty(name))
1157 return false;
1158
1159 /* Interface names must start with a letter, uppercase or lower case, but nothing else */
1160 if (!strchr(LETTERS, name[0]))
1161 return false;
1162
1163 /* Otherwise it may be a series of non-empty dot separated labels, which are alphanumerical and may
1164 * contain single dashes in the middle */
1165 bool dot = false, dash = false;
1166 for (const char *c = name + 1; *c; c++) {
1167 switch (*c) {
1168
1169 case '.':
1170 if (dot || dash)
1171 return false;
1172
1173 dot = true;
1174 break;
1175
1176 case '-':
1177 if (dot || dash)
1178 return false;
1179
1180 dash = true;
1181 break;
1182
1183 default:
1184 if (!strchr(ALPHANUMERICAL, *c))
1185 return false;
1186
1187 dot = dash = false;
1188 }
1189 }
1190
1191 if (dot || dash)
1192 return false;
1193
1194 return true;
1195}
1196
a972870e 1197static int varlink_idl_symbol_consistent(const VarlinkInterface *interface, const VarlinkSymbol *symbol, int level);
e50b2a93
LP
1198
1199static int varlink_idl_field_consistent(
1200 const VarlinkInterface *interface,
1201 const VarlinkSymbol *symbol,
1202 const VarlinkField *field,
a972870e 1203 int level) {
e50b2a93
LP
1204
1205 const char *symbol_name;
1206 int r;
1207
1208 assert(interface);
1209 assert(symbol);
1210 assert(field);
1211 assert(field->name);
1212
1213 symbol_name = symbol->name ?: "<anonymous>";
1214
1215 if (field->field_type <= 0 || field->field_type >= _VARLINK_FIELD_TYPE_MAX)
1216 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Field type for '%s' in symbol '%s' is not valid, refusing.", field->name, symbol_name);
1217
1218 if (field->field_type == VARLINK_ENUM_VALUE) {
1219
1220 if (symbol->symbol_type != VARLINK_ENUM_TYPE)
1221 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Enum field type for '%s' in non-enum symbol '%s', refusing.", field->name, symbol_name);
1222
1223 if (field->field_flags != 0)
1224 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Enum field '%s' in symbol '%s' has non-zero flags set, refusing.", field->name, symbol_name);
1225 } else {
1226 if (symbol->symbol_type == VARLINK_ENUM_TYPE)
1227 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Non-enum field type for '%s' in enum symbol '%s', refusing.", field->name, symbol_name);
1228
1229 if (!IN_SET(field->field_flags & ~VARLINK_NULLABLE, 0, VARLINK_ARRAY, VARLINK_MAP))
1230 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Flags of field '%s' in symbol '%s' is invalid, refusing.", field->name, symbol_name);
1231 }
1232
1233 if (symbol->symbol_type != VARLINK_METHOD) {
1234 if (field->field_direction != VARLINK_REGULAR)
1235 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Direction of '%s' in non-method symbol '%s' not regular, refusing.", field->name, symbol_name);
1236 } else {
1237 if (!IN_SET(field->field_direction, VARLINK_INPUT, VARLINK_OUTPUT))
1238 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Direction of '%s' in method symbol '%s' is not input or output, refusing.", field->name, symbol_name);
1239 }
1240
1241 if (field->symbol) {
1242 if (!IN_SET(field->field_type, VARLINK_STRUCT, VARLINK_ENUM, VARLINK_NAMED_TYPE))
1243 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Target symbol for field '%s' in symbol '%s' defined for elemental field, refusing.", field->name, symbol_name);
1244
1245 if (field->field_type == VARLINK_NAMED_TYPE) {
1246 const VarlinkSymbol *found;
1247
1248 if (!field->symbol->name || !field->named_type || !streq(field->symbol->name, field->named_type))
1249 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol name and named type of field '%s' in symbol '%s' do do not match, refusing.", field->name, symbol_name);
1250
1251 /* If this is a named type, then check if it's properly part of the interface */
1252 found = varlink_idl_find_symbol(interface, _VARLINK_SYMBOL_TYPE_INVALID, field->symbol->name);
1253 if (!found)
1254 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol of named type of field '%s' in symbol '%s' is not part of the interface, refusing.", field->name, symbol_name);
1255
1256 if (!IN_SET(found->symbol_type, VARLINK_ENUM_TYPE, VARLINK_STRUCT_TYPE))
1257 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol of named type of field '%s' in symbol '%s' is not a type, refusing.", field->name, symbol_name);
1258 } else {
1259 /* If this is an anonymous type, then we recursively check if it's consistent, since
1260 * it's not part of the interface, and hence we won't validate it from there. */
1261
1262 r = varlink_idl_symbol_consistent(interface, field->symbol, level);
1263 if (r < 0)
1264 return r;
1265 }
1266
1267 } else {
1268 if (IN_SET(field->field_type, VARLINK_STRUCT, VARLINK_ENUM, VARLINK_NAMED_TYPE))
1269 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "No target symbol for field '%s' in symbol '%s' defined for elemental field, refusing.", field->name, symbol_name);
1270
1271 if (field->named_type)
1272 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Unresolved symbol in field '%s' in symbol '%s', refusing.", field->name, symbol_name);
1273 }
1274
1275 if (field->named_type) {
1276 if (field->field_type != VARLINK_NAMED_TYPE)
1277 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Named type set for field '%s' in symbol '%s' but not a named type field, refusing.", field->name, symbol_name);
1278 } else {
1279 if (field->field_type == VARLINK_NAMED_TYPE)
1280 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "No named type set for field '%s' in symbol '%s' but field is a named type field, refusing.", field->name, symbol_name);
1281 }
1282
1283 return 0;
1284}
1285
1286static bool varlink_symbol_is_empty(const VarlinkSymbol *symbol) {
1287 assert(symbol);
1288
1289 return symbol->fields[0].field_type == _VARLINK_FIELD_TYPE_END_MARKER;
1290}
1291
1292static int varlink_idl_symbol_consistent(
1293 const VarlinkInterface *interface,
1294 const VarlinkSymbol *symbol,
a972870e 1295 int level) {
e50b2a93
LP
1296
1297 _cleanup_(set_freep) Set *input_set = NULL, *output_set = NULL;
1298 const char *symbol_name;
1299 int r;
1300
1301 assert(interface);
1302 assert(symbol);
1303
1304 symbol_name = symbol->name ?: "<anonymous>";
1305
1306 if (symbol->symbol_type < 0 || symbol->symbol_type >= _VARLINK_SYMBOL_TYPE_MAX)
1307 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol type for '%s' is not valid, refusing.", symbol_name);
1308
1309 if (IN_SET(symbol->symbol_type, VARLINK_STRUCT_TYPE, VARLINK_ENUM_TYPE) && varlink_symbol_is_empty(symbol))
1310 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol '%s' is empty, refusing.", symbol_name);
1311
1312 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
1313 Set **name_set = field->field_direction == VARLINK_OUTPUT ? &output_set : &input_set; /* for the method case we need two separate sets, otherwise we use the same */
1314
1315 if (!varlink_idl_field_name_is_valid(field->name))
1316 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Field name '%s' in symbol '%s' not valid, refusing.", field->name, symbol_name);
1317
1318 if (set_contains(*name_set, field->name))
1319 return log_full_errno(level, SYNTHETIC_ERRNO(ENOTUNIQ), "Field '%s' defined twice in symbol '%s', refusing.", field->name, symbol_name);
1320
1321 if (set_ensure_put(name_set, &string_hash_ops, field->name) < 0)
1322 return log_oom();
1323
1324 r = varlink_idl_field_consistent(interface, symbol, field, level);
1325 if (r < 0)
1326 return r;
1327 }
1328
1329 return 0;
1330}
1331
a972870e 1332int varlink_idl_consistent(const VarlinkInterface *interface, int level) {
e50b2a93
LP
1333 _cleanup_(set_freep) Set *name_set = NULL;
1334 int r;
1335
1336 assert(interface);
1337
1338 if (!varlink_idl_interface_name_is_valid(interface->name))
1339 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Interface name '%s' is not valid, refusing.", interface->name);
1340
1341 for (const VarlinkSymbol *const *symbol = interface->symbols; *symbol; symbol++) {
1342
1343 if (!varlink_idl_symbol_name_is_valid((*symbol)->name))
1344 return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol name '%s' is not valid, refusing.", strempty((*symbol)->name));
1345
1346 if (set_contains(name_set, (*symbol)->name))
1347 return log_full_errno(level, SYNTHETIC_ERRNO(ENOTUNIQ), "Symbol '%s' defined twice in interface, refusing.", (*symbol)->name);
1348
1349 if (set_ensure_put(&name_set, &string_hash_ops, (*symbol)->name) < 0)
1350 return log_oom();
1351
1352 r = varlink_idl_symbol_consistent(interface, *symbol, level);
1353 if (r < 0)
1354 return r;
1355 }
1356
1357 return 0;
1358}
1359
1360static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant *v, VarlinkFieldDirection direction, const char **bad_field);
1361
1362static int varlink_idl_validate_field_element_type(const VarlinkField *field, JsonVariant *v) {
1363 assert(field);
1364
1365 switch (field->field_type) {
1366
1367 case VARLINK_STRUCT:
1368 case VARLINK_ENUM:
1369 case VARLINK_NAMED_TYPE:
1370 return varlink_idl_validate_symbol(field->symbol, v, VARLINK_REGULAR, NULL);
1371
1372 case VARLINK_BOOL:
1373 if (!json_variant_is_boolean(v))
1374 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a bool, but it is not, refusing.", strna(field->name));
1375
1376 break;
1377
1378 case VARLINK_INT:
1379 if (!json_variant_is_integer(v) && !json_variant_is_unsigned(v))
1380 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an int, but it is not, refusing.", strna(field->name));
1381
1382 break;
1383
1384 case VARLINK_FLOAT:
1385 if (!json_variant_is_number(v))
1386 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a float, but it is not, refusing.", strna(field->name));
1387
1388 break;
1389
1390 case VARLINK_STRING:
1391 if (!json_variant_is_string(v))
1392 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a string, but it is not, refusing.", strna(field->name));
1393
1394 break;
1395
1396 case VARLINK_OBJECT:
1397 if (!json_variant_is_object(v))
1398 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an object, but it is not, refusing.", strna(field->name));
1399
1400 break;
1401
1402 default:
1403 assert_not_reached();
1404 }
1405
1406 return 0;
1407}
1408
1409static int varlink_idl_validate_field(const VarlinkField *field, JsonVariant *v) {
1410 int r;
1411
1412 assert(field);
1413
1414 if (!v || json_variant_is_null(v)) {
1415
1416 if (!FLAGS_SET(field->field_flags, VARLINK_NULLABLE))
1417 return log_debug_errno(SYNTHETIC_ERRNO(ENOANO), "Mandatory field '%s' is null or missing on object, refusing.", strna(field->name));
1418
1419 } else if (FLAGS_SET(field->field_flags, VARLINK_ARRAY)) {
1420 JsonVariant *i;
1421
1422 if (!json_variant_is_array(v))
1423 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an array, but it is not, refusing.", strna(field->name));
1424
1425 JSON_VARIANT_ARRAY_FOREACH(i, v) {
1426 r = varlink_idl_validate_field_element_type(field, i);
1427 if (r < 0)
1428 return r;
1429 }
1430
1431 } else if (FLAGS_SET(field->field_flags, VARLINK_MAP)) {
1432 _unused_ const char *k;
1433 JsonVariant *e;
1434
1435 if (!json_variant_is_object(v))
1436 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an object, but it is not, refusing.", strna(field->name));
1437
1438 JSON_VARIANT_OBJECT_FOREACH(k, e, v) {
1439 r = varlink_idl_validate_field_element_type(field, e);
1440 if (r < 0)
1441 return r;
1442 }
1443 } else {
1444
1445 r = varlink_idl_validate_field_element_type(field, v);
1446 if (r < 0)
1447 return r;
1448 }
1449
1450 return 0;
1451}
1452
1453static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant *v, VarlinkFieldDirection direction, const char **bad_field) {
1454 int r;
1455
1456 assert(symbol);
1457
1458 if (!v) {
1459 if (bad_field)
1460 *bad_field = NULL;
1461 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Null object passed, refusing.");
1462 }
1463
1464 switch (symbol->symbol_type) {
1465
1466 case VARLINK_ENUM_TYPE: {
1467 bool found = false;
1468 const char *s;
1469
1470 if (!json_variant_is_string(v)) {
1471 if (bad_field)
1472 *bad_field = symbol->name;
1473 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-string to enum field '%s', refusing.", strna(symbol->name));
1474 }
1475
1476 assert_se(s = json_variant_string(v));
1477
1478 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
1479
1480 assert(field->field_type == VARLINK_ENUM_VALUE);
1481
1482 if (streq_ptr(field->name, s)) {
1483 found = true;
1484 break;
1485 }
1486 }
1487
1488 if (!found) {
1489 if (bad_field)
1490 *bad_field = s;
1491 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed unrecognized string '%s' to enum field '%s', refusing.", s, strna(symbol->name));
1492 }
1493
1494 break;
1495 }
1496
1497 case VARLINK_STRUCT_TYPE:
1498 case VARLINK_METHOD:
1499 case VARLINK_ERROR: {
1500 if (!json_variant_is_object(v)) {
1501 if (bad_field)
1502 *bad_field = symbol->name;
1503 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-object to field '%s', refusing.", strna(symbol->name));
1504 }
1505
1506 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
1507
1508 if (field->field_direction != direction)
1509 continue;
1510
1511 r = varlink_idl_validate_field(field, json_variant_by_key(v, field->name));
1512 if (r < 0) {
1513 if (bad_field)
1514 *bad_field = field->name;
1515 return r;
1516 }
1517 }
1518
1519 _unused_ JsonVariant *e;
1520 const char *name;
1521 JSON_VARIANT_OBJECT_FOREACH(name, e, v) {
1522 if (!varlink_idl_find_field(symbol, name)) {
1523 if (bad_field)
1524 *bad_field = name;
1525 return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Field '%s' not defined for object, refusing.", name);
1526 }
1527 }
1528
1529 break;
1530 }
1531
1532 default:
1533 assert_not_reached();
1534 }
1535
1536 return 1; /* validated */
1537}
1538
1539static int varlink_idl_validate_method(const VarlinkSymbol *method, JsonVariant *v, VarlinkFieldDirection direction, const char **bad_field) {
1540 assert(IN_SET(direction, VARLINK_INPUT, VARLINK_OUTPUT));
1541
1542 if (!method)
1543 return 0; /* Can't validate */
1544 if (method->symbol_type != VARLINK_METHOD)
1545 return -EBADMSG;
1546
1547 return varlink_idl_validate_symbol(method, v, direction, bad_field);
1548}
1549
1550int varlink_idl_validate_method_call(const VarlinkSymbol *method, JsonVariant *v, const char **bad_field) {
1551 return varlink_idl_validate_method(method, v, VARLINK_INPUT, bad_field);
1552}
1553
1554int varlink_idl_validate_method_reply(const VarlinkSymbol *method, JsonVariant *v, const char **bad_field) {
1555 return varlink_idl_validate_method(method, v, VARLINK_OUTPUT, bad_field);
1556}
1557
1558int varlink_idl_validate_error(const VarlinkSymbol *error, JsonVariant *v, const char **bad_field) {
1559 if (!error)
1560 return 0; /* Can't validate */
1561 if (error->symbol_type != VARLINK_ERROR)
1562 return -EBADMSG;
1563
1564 return varlink_idl_validate_symbol(error, v, VARLINK_REGULAR, bad_field);
1565}
1566
1567const VarlinkSymbol* varlink_idl_find_symbol(
1568 const VarlinkInterface *interface,
1569 VarlinkSymbolType type,
1570 const char *name) {
1571
1572 assert(interface);
1573 assert(type < _VARLINK_SYMBOL_TYPE_MAX);
1574
1575 if (isempty(name))
1576 return NULL;
1577
1578 for (const VarlinkSymbol *const*symbol = interface->symbols; *symbol; symbol++) {
1579 if (type >= 0 && (*symbol)->symbol_type != type)
1580 continue;
1581
1582 if (streq_ptr((*symbol)->name, name))
1583 return *symbol;
1584 }
1585
1586 return NULL;
1587}
1588
1589const VarlinkField* varlink_idl_find_field(
1590 const VarlinkSymbol *symbol,
1591 const char *name) {
1592
1593 assert(symbol);
1594
1595 if (isempty(name))
1596 return NULL;
1597
1598 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++)
1599 if (streq_ptr(field->name, name))
1600 return field;
1601
1602 return NULL;
1603}