1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "memstream-util.h"
5 #include "terminal-util.h"
6 #include "varlink-idl.h"
12 COLOR_SYMBOL_TYPE
, /* interface, method, type, error */
13 COLOR_FIELD_TYPE
, /* string, bool, … */
15 COLOR_MARKS
, /* [], ->, ?, … */
20 static int varlink_idl_format_all_fields(FILE *f
, const VarlinkSymbol
*symbol
, VarlinkFieldDirection direction
, const char *indent
, const char *const colors
[static _COLOR_MAX
]);
22 static int varlink_idl_format_enum_values(
24 const VarlinkSymbol
*symbol
,
26 const char *const colors
[static _COLOR_MAX
]) {
32 assert(symbol
->symbol_type
== VARLINK_ENUM_TYPE
);
34 for (const VarlinkField
*field
= symbol
->fields
; field
->field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; field
++) {
42 fputs(strempty(indent
), f
);
44 fputs(colors
[COLOR_IDENTIFIER
], f
);
45 fputs(field
->name
, f
);
46 fputs(colors
[COLOR_RESET
], f
);
53 fputs(strempty(indent
), f
);
60 static int varlink_idl_format_field(
62 const VarlinkField
*field
,
64 const char *const colors
[static _COLOR_MAX
]) {
69 fputs(strempty(indent
), f
);
70 fputs(colors
[COLOR_IDENTIFIER
], f
);
71 fputs(field
->name
, f
);
72 fputs(colors
[COLOR_RESET
], f
);
75 if (FLAGS_SET(field
->field_flags
, VARLINK_NULLABLE
)) {
76 fputs(colors
[COLOR_MARKS
], f
);
78 fputs(colors
[COLOR_RESET
], f
);
81 switch (field
->field_flags
& (VARLINK_MAP
|VARLINK_ARRAY
)) {
84 fputs(colors
[COLOR_MARKS
], f
);
86 fputs(colors
[COLOR_FIELD_TYPE
], f
);
88 fputs(colors
[COLOR_MARKS
], f
);
90 fputs(colors
[COLOR_RESET
], f
);
94 fputs(colors
[COLOR_MARKS
], f
);
96 fputs(colors
[COLOR_RESET
], f
);
103 assert_not_reached();
106 switch (field
->field_type
) {
109 fputs(colors
[COLOR_FIELD_TYPE
], f
);
111 fputs(colors
[COLOR_RESET
], f
);
115 fputs(colors
[COLOR_FIELD_TYPE
], f
);
117 fputs(colors
[COLOR_RESET
], f
);
121 fputs(colors
[COLOR_FIELD_TYPE
], f
);
123 fputs(colors
[COLOR_RESET
], f
);
127 fputs(colors
[COLOR_FIELD_TYPE
], f
);
129 fputs(colors
[COLOR_RESET
], f
);
133 fputs(colors
[COLOR_FIELD_TYPE
], f
);
135 fputs(colors
[COLOR_RESET
], f
);
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
);
145 return varlink_idl_format_all_fields(f
, ASSERT_PTR(field
->symbol
), VARLINK_REGULAR
, indent
, colors
);
148 return varlink_idl_format_enum_values(f
, ASSERT_PTR(field
->symbol
), indent
, colors
);
151 assert_not_reached();
157 static int varlink_idl_format_all_fields(
159 const VarlinkSymbol
*symbol
,
160 VarlinkFieldDirection filter_direction
,
162 const char *const colors
[static _COLOR_MAX
]) {
164 _cleanup_free_
char *indent2
= NULL
;
170 assert(IN_SET(symbol
->symbol_type
, VARLINK_STRUCT_TYPE
, VARLINK_METHOD
, VARLINK_ERROR
));
172 indent2
= strjoin(strempty(indent
), "\t");
176 for (const VarlinkField
*field
= symbol
->fields
; field
->field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; field
++) {
178 if (field
->field_direction
!= filter_direction
)
187 r
= varlink_idl_format_field(f
, field
, indent2
, colors
);
196 fputs(strempty(indent
), f
);
203 static int varlink_idl_format_symbol(
205 const VarlinkSymbol
*symbol
,
206 const char *const colors
[static _COLOR_MAX
]) {
212 switch (symbol
->symbol_type
) {
214 case VARLINK_ENUM_TYPE
:
215 fputs(colors
[COLOR_SYMBOL_TYPE
], f
);
217 fputs(colors
[COLOR_IDENTIFIER
], f
);
218 fputs(symbol
->name
, f
);
219 fputs(colors
[COLOR_RESET
], f
);
221 r
= varlink_idl_format_enum_values(f
, symbol
, /* indent= */ NULL
, colors
);
224 case VARLINK_STRUCT_TYPE
:
225 fputs(colors
[COLOR_SYMBOL_TYPE
], f
);
227 fputs(colors
[COLOR_IDENTIFIER
], f
);
228 fputs(symbol
->name
, f
);
229 fputs(colors
[COLOR_RESET
], f
);
231 r
= varlink_idl_format_all_fields(f
, symbol
, VARLINK_REGULAR
, /* indent= */ NULL
, colors
);
235 fputs(colors
[COLOR_SYMBOL_TYPE
], f
);
237 fputs(colors
[COLOR_IDENTIFIER
], f
);
238 fputs(symbol
->name
, f
);
239 fputs(colors
[COLOR_RESET
], f
);
241 r
= varlink_idl_format_all_fields(f
, symbol
, VARLINK_INPUT
, /* indent= */ NULL
, colors
);
245 fputs(colors
[COLOR_MARKS
], f
);
247 fputs(colors
[COLOR_RESET
], f
);
249 r
= varlink_idl_format_all_fields(f
, symbol
, VARLINK_OUTPUT
, /* indent= */ NULL
, colors
);
253 fputs(colors
[COLOR_SYMBOL_TYPE
], f
);
255 fputs(colors
[COLOR_IDENTIFIER
], f
);
256 fputs(symbol
->name
, f
);
257 fputs(colors
[COLOR_RESET
], f
);
259 r
= varlink_idl_format_all_fields(f
, symbol
, VARLINK_REGULAR
, /* indent= */ NULL
, colors
);
263 assert_not_reached();
272 static int varlink_idl_format_all_symbols(
274 const VarlinkInterface
*interface
,
275 VarlinkSymbolType filter_type
,
276 const char *const colors
[static _COLOR_MAX
]) {
283 for (const VarlinkSymbol
*const*symbol
= interface
->symbols
; *symbol
; symbol
++) {
285 if ((*symbol
)->symbol_type
!= filter_type
)
290 r
= varlink_idl_format_symbol(f
, *symbol
, colors
);
298 int 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
,
307 static const char* const color_off
[_COLOR_MAX
] = {
319 use_colors
= colors_enabled();
321 const char *const *colors
= use_colors
? color_table
: color_off
;
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
);
330 for (VarlinkSymbolType t
= 0; t
< _VARLINK_SYMBOL_TYPE_MAX
; t
++) {
331 r
= varlink_idl_format_all_symbols(f
, interface
, t
, colors
);
339 int varlink_idl_format(const VarlinkInterface
*interface
, char **ret
) {
340 _cleanup_(memstream_done
) MemStream memstream
= {};
343 if (!memstream_init(&memstream
))
346 r
= varlink_idl_dump(memstream
.f
, /* use_colors= */ false, interface
);
350 return memstream_finalize(&memstream
, ret
, NULL
);
353 static VarlinkSymbol
*varlink_symbol_free(VarlinkSymbol
*symbol
) {
357 /* See comment in varlink_interface_free() regarding the casting away of `const` */
359 free((char*) symbol
->name
);
361 for (size_t i
= 0; symbol
->fields
[i
].field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; i
++) {
362 VarlinkField
*field
= symbol
->fields
+ i
;
364 free((void*) field
->name
);
365 free((void*) field
->named_type
);
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
370 if (field
->symbol
&& field
->field_type
!= VARLINK_NAMED_TYPE
)
371 varlink_symbol_free((VarlinkSymbol
*) field
->symbol
);
374 return mfree(symbol
);
377 VarlinkInterface
* varlink_interface_free(VarlinkInterface
*interface
) {
381 /* So here's the thing: in most cases we want that users of this define their interface descriptions
382 * in C code, and hence the definitions are constant and immutable during the lifecycle of the
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. */
389 free((char*) interface
->name
);
391 for (size_t i
= 0; interface
->symbols
[i
]; i
++)
392 varlink_symbol_free((VarlinkSymbol
*) interface
->symbols
[i
]);
394 return mfree(interface
);
397 DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkSymbol
*, varlink_symbol_free
);
399 static int varlink_interface_realloc(VarlinkInterface
**interface
, size_t n_symbols
) {
404 n_symbols
++; /* Space for trailing NULL end marker symbol */
407 if (n_symbols
> (SIZE_MAX
- offsetof(VarlinkInterface
, symbols
)) / sizeof(VarlinkSymbol
*))
410 n
= realloc0(*interface
, offsetof(VarlinkInterface
, symbols
) + sizeof(VarlinkSymbol
*) * n_symbols
);
418 static int varlink_symbol_realloc(VarlinkSymbol
**symbol
, size_t n_fields
) {
423 n_fields
++; /* Space for trailing end marker field */
426 if (n_fields
> (SIZE_MAX
- offsetof(VarlinkSymbol
, fields
)) / sizeof(VarlinkField
))
429 n
= realloc0(*symbol
, offsetof(VarlinkSymbol
, fields
) + sizeof(VarlinkField
) * n_fields
);
437 #define VALID_CHARS_IDENTIFIER ALPHANUMERICAL "_"
438 #define VALID_CHARS_RESERVED LOWERCASE_LETTERS
439 #define VALID_CHARS_INTERFACE_NAME ALPHANUMERICAL ".-"
441 static void advance_line_column(const char *p
, size_t n
, unsigned *line
, unsigned *column
) {
447 for (; n
> 0; p
++, n
--) {
457 static size_t token_match(
459 const char *allowed_delimiters
,
460 const char *allowed_chars
) {
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. */
467 if (allowed_delimiters
&& strchr(allowed_delimiters
, *p
))
473 return strspn(p
, allowed_chars
);
476 static int varlink_idl_subparse_token(
480 const char *allowed_delimiters
,
481 const char *allowed_chars
,
484 _cleanup_free_
char *t
= NULL
;
493 if (**p
== '\0') { /* eof */
498 l
= token_match(*p
, allowed_delimiters
, allowed_chars
);
500 /* No token of the permitted character set found? Then let's try to skip over whitespace and try again */
504 ll
= strspn(*p
, WHITESPACE
);
505 advance_line_column(*p
, ll
, line
, column
);
508 if (**p
== '\0') { /* eof */
513 l
= token_match(*p
, allowed_delimiters
, allowed_chars
);
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
));
522 advance_line_column(*p
, l
, line
, column
);
525 *ret_token
= TAKE_PTR(t
);
529 static int varlink_idl_subparse_comment(
541 l
= strcspn(*p
, NEWLINE
);
542 advance_line_column(*p
, l
+ 1, line
, column
);
548 static int varlink_idl_subparse_whitespace(
560 l
= strspn(*p
, WHITESPACE
);
561 advance_line_column(*p
, l
, line
, column
);
567 static int varlink_idl_subparse_struct_or_enum(const char **p
, unsigned *line
, unsigned *column
, VarlinkSymbol
**symbol
, size_t *n_fields
, VarlinkFieldDirection direction
, unsigned depth
);
569 static int varlink_idl_subparse_field_type(
584 r
= varlink_idl_subparse_whitespace(p
, line
, column
);
588 if (startswith(*p
, "?")) {
589 field
->field_flags
|= VARLINK_NULLABLE
;
592 field
->field_flags
&= ~VARLINK_NULLABLE
;
596 advance_line_column(*p
, l
, line
, column
);
599 if (startswith(*p
, "[]")) {
601 field
->field_flags
= (field
->field_flags
& ~VARLINK_MAP
) | VARLINK_ARRAY
;
602 } else if (startswith(*p
, "[string]")) {
604 field
->field_flags
= (field
->field_flags
& ~VARLINK_ARRAY
) | VARLINK_MAP
;
607 field
->field_flags
= field
->field_flags
& ~(VARLINK_MAP
| VARLINK_ARRAY
);
610 advance_line_column(*p
, l
, line
, column
);
613 if (startswith(*p
, "bool")) {
615 field
->field_type
= VARLINK_BOOL
;
616 } else if (startswith(*p
, "int")) {
618 field
->field_type
= VARLINK_INT
;
619 } else if (startswith(*p
, "float")) {
621 field
->field_type
= VARLINK_FLOAT
;
622 } else if (startswith(*p
, "string")) {
624 field
->field_type
= VARLINK_STRING
;
625 } else if (startswith(*p
, "object")) {
627 field
->field_type
= VARLINK_OBJECT
;
628 } else if (**p
== '(') {
629 _cleanup_(varlink_symbol_freep
) VarlinkSymbol
*symbol
= NULL
;
632 r
= varlink_symbol_realloc(&symbol
, n_fields
);
636 symbol
->symbol_type
= _VARLINK_SYMBOL_TYPE_INVALID
;
638 r
= varlink_idl_subparse_struct_or_enum(
649 if (symbol
->symbol_type
== VARLINK_STRUCT_TYPE
)
650 field
->field_type
= VARLINK_STRUCT
;
652 assert(symbol
->symbol_type
== VARLINK_ENUM_TYPE
);
653 field
->field_type
= VARLINK_ENUM
;
656 field
->symbol
= TAKE_PTR(symbol
);
659 _cleanup_free_
char *token
= NULL
;
661 r
= varlink_idl_subparse_token(p
, line
, column
, /* valid_tokens= */ NULL
, VALID_CHARS_IDENTIFIER
, &token
);
665 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
667 field
->named_type
= TAKE_PTR(token
);
668 field
->field_type
= VARLINK_NAMED_TYPE
;
672 advance_line_column(*p
, l
, line
, column
);
678 static int varlink_idl_subparse_struct_or_enum(
682 VarlinkSymbol
**symbol
,
684 VarlinkFieldDirection direction
,
693 } state
= STATE_OPEN
;
694 _cleanup_free_
char *field_name
= NULL
;
695 const char *allowed_delimiters
= "(", *allowed_chars
= NULL
;
706 if (depth
> DEPTH_MAX
)
707 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Maximum nesting depth reached (%u).", *line
, *column
, DEPTH_MAX
);
709 while (state
!= STATE_DONE
) {
710 _cleanup_free_
char *token
= NULL
;
712 r
= varlink_idl_subparse_token(
726 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
727 if (!streq(token
, "("))
728 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Unexpected token '%s'.", *line
, *column
, token
);
731 allowed_delimiters
= ")";
732 allowed_chars
= VALID_CHARS_IDENTIFIER
;
739 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
740 if (streq(token
, ")"))
743 field_name
= TAKE_PTR(token
);
745 allowed_delimiters
= ":,)";
746 allowed_chars
= NULL
;
755 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
757 if (streq(token
, ":")) {
760 if ((*symbol
)->symbol_type
< 0)
761 (*symbol
)->symbol_type
= VARLINK_STRUCT_TYPE
;
762 if ((*symbol
)->symbol_type
== VARLINK_ENUM_TYPE
)
763 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Enum with struct fields, refusing.", *line
, *column
);
765 r
= varlink_symbol_realloc(symbol
, *n_fields
+ 1);
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
,
776 r
= varlink_idl_subparse_field_type(p
, line
, column
, field
, depth
);
781 allowed_delimiters
= ",)";
782 allowed_chars
= NULL
;
784 } else if (STR_IN_SET(token
, ",", ")")) {
787 if ((*symbol
)->symbol_type
< 0)
788 (*symbol
)->symbol_type
= VARLINK_ENUM_TYPE
;
789 if ((*symbol
)->symbol_type
!= VARLINK_ENUM_TYPE
)
790 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Struct with enum fields, refusing.", *line
, *column
);
792 r
= varlink_symbol_realloc(symbol
, *n_fields
+ 1);
796 field
= (*symbol
)->fields
+ (*n_fields
)++;
797 *field
= (VarlinkField
) {
798 .name
= TAKE_PTR(field_name
),
799 .field_type
= VARLINK_ENUM_VALUE
,
802 if (streq(token
, ",")) {
804 allowed_delimiters
= NULL
;
805 allowed_chars
= VALID_CHARS_IDENTIFIER
;
807 assert(streq(token
, ")"));
811 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Unexpected token '%s'.", *line
, *column
, token
);
819 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
820 if (streq(token
, ",")) {
822 allowed_delimiters
= NULL
;
823 allowed_chars
= VALID_CHARS_IDENTIFIER
;
824 } else if (streq(token
, ")"))
827 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Unexpected token '%s'.", *line
, *column
, token
);
831 assert_not_reached();
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
);
843 static int varlink_idl_resolve_symbol_types(VarlinkInterface
*interface
, VarlinkSymbol
*symbol
) {
847 for (VarlinkField
*field
= symbol
->fields
; field
->field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; field
++) {
848 const VarlinkSymbol
*found
;
850 if (field
->field_type
!= VARLINK_NAMED_TYPE
)
853 if (field
->symbol
) /* Already resolved */
856 if (!field
->named_type
)
857 return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH
), "Named type field lacking a type name.");
859 found
= varlink_idl_find_symbol(interface
, _VARLINK_SYMBOL_TYPE_INVALID
, field
->named_type
);
861 return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH
), "Failed to find type '%s'.", field
->named_type
);
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
);
866 field
->symbol
= found
;
872 static int varlink_idl_resolve_types(VarlinkInterface
*interface
) {
877 for (VarlinkSymbol
**symbol
= (VarlinkSymbol
**) interface
->symbols
; *symbol
; symbol
++) {
878 r
= varlink_idl_resolve_symbol_types(interface
, *symbol
);
886 int varlink_idl_parse(
890 VarlinkInterface
**ret
) {
892 _cleanup_(varlink_interface_freep
) VarlinkInterface
*interface
= NULL
;
893 _cleanup_(varlink_symbol_freep
) VarlinkSymbol
*symbol
= NULL
;
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
;
918 while (state
!= STATE_DONE
) {
919 _cleanup_free_
char *token
= NULL
;
921 r
= varlink_idl_subparse_token(
933 case STATE_PRE_INTERFACE
:
935 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
936 if (streq(token
, "#")) {
937 r
= varlink_idl_subparse_comment(&text
, line
, column
);
940 } else if (streq(token
, "interface")) {
941 state
= STATE_INTERFACE
;
942 allowed_delimiters
= NULL
;
943 allowed_chars
= VALID_CHARS_INTERFACE_NAME
;
945 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Unexpected token '%s'.", *line
, *column
, token
);
948 case STATE_INTERFACE
:
950 assert(n_symbols
== 0);
953 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
955 r
= varlink_interface_realloc(&interface
, n_symbols
);
959 interface
->name
= TAKE_PTR(token
);
960 state
= STATE_PRE_SYMBOL
;
961 allowed_delimiters
= "#";
962 allowed_chars
= VALID_CHARS_RESERVED
;
965 case STATE_PRE_SYMBOL
:
971 if (streq(token
, "#")) {
972 r
= varlink_idl_subparse_comment(&text
, line
, column
);
975 } else if (streq(token
, "method")) {
976 state
= STATE_METHOD
;
977 allowed_chars
= VALID_CHARS_IDENTIFIER
;
978 } else if (streq(token
, "type")) {
980 allowed_chars
= VALID_CHARS_IDENTIFIER
;
981 } else if (streq(token
, "error")) {
983 allowed_chars
= VALID_CHARS_IDENTIFIER
;
985 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Unexpected token '%s'.", *line
, *column
, token
);
994 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
996 r
= varlink_symbol_realloc(&symbol
, n_fields
);
1000 symbol
->symbol_type
= VARLINK_METHOD
;
1001 symbol
->name
= TAKE_PTR(token
);
1003 r
= varlink_idl_subparse_struct_or_enum(&text
, line
, column
, &symbol
, &n_fields
, VARLINK_INPUT
, 0);
1007 state
= STATE_METHOD_ARROW
;
1008 allowed_chars
= "->";
1011 case STATE_METHOD_ARROW
:
1015 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
1017 if (!streq(token
, "->"))
1018 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Unexpected token '%s'.", *line
, *column
, token
);
1020 r
= varlink_idl_subparse_struct_or_enum(&text
, line
, column
, &symbol
, &n_fields
, VARLINK_OUTPUT
, 0);
1024 r
= varlink_interface_realloc(&interface
, n_symbols
+ 1);
1028 interface
->symbols
[n_symbols
++] = TAKE_PTR(symbol
);
1030 state
= STATE_PRE_SYMBOL
;
1031 allowed_chars
= VALID_CHARS_RESERVED
"#";
1039 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
1041 r
= varlink_symbol_realloc(&symbol
, n_fields
);
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() */
1046 symbol
->name
= TAKE_PTR(token
);
1048 r
= varlink_idl_subparse_struct_or_enum(&text
, line
, column
, &symbol
, &n_fields
, VARLINK_REGULAR
, 0);
1052 r
= varlink_interface_realloc(&interface
, n_symbols
+ 1);
1056 interface
->symbols
[n_symbols
++] = TAKE_PTR(symbol
);
1058 state
= STATE_PRE_SYMBOL
;
1059 allowed_chars
= VALID_CHARS_RESERVED
"#";
1067 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "%u:%u: Premature EOF.", *line
, *column
);
1069 r
= varlink_symbol_realloc(&symbol
, n_fields
);
1073 symbol
->symbol_type
= VARLINK_ERROR
;
1074 symbol
->name
= TAKE_PTR(token
);
1076 r
= varlink_idl_subparse_struct_or_enum(&text
, line
, column
, &symbol
, &n_fields
, VARLINK_REGULAR
, 0);
1080 r
= varlink_interface_realloc(&interface
, n_symbols
+ 1);
1084 interface
->symbols
[n_symbols
++] = TAKE_PTR(symbol
);
1086 state
= STATE_PRE_SYMBOL
;
1087 allowed_chars
= VALID_CHARS_RESERVED
"#";
1091 assert_not_reached();
1095 r
= varlink_idl_resolve_types(interface
);
1099 *ret
= TAKE_PTR(interface
);
1103 bool varlink_idl_field_name_is_valid(const char *name
) {
1107 /* Field names may start with lower or uppercase char, but no numerals or underscore */
1108 if (!strchr(LETTERS
, name
[0]))
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
++) {
1123 if (!strchr(ALPHANUMERICAL
, *c
))
1135 bool varlink_idl_symbol_name_is_valid(const char *name
) {
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"))
1144 /* Symbols must be named with an uppercase letter as first character */
1145 if (!strchr(UPPERCASE_LETTERS
, name
[0]))
1148 for (const char *c
= name
+ 1; *c
; c
++)
1149 if (!strchr(ALPHANUMERICAL
, *c
))
1155 bool varlink_idl_interface_name_is_valid(const char *name
) {
1159 /* Interface names must start with a letter, uppercase or lower case, but nothing else */
1160 if (!strchr(LETTERS
, name
[0]))
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
++) {
1184 if (!strchr(ALPHANUMERICAL
, *c
))
1197 static int varlink_idl_symbol_consistent(const VarlinkInterface
*interface
, const VarlinkSymbol
*symbol
, int level
);
1199 static int varlink_idl_field_consistent(
1200 const VarlinkInterface
*interface
,
1201 const VarlinkSymbol
*symbol
,
1202 const VarlinkField
*field
,
1205 const char *symbol_name
;
1211 assert(field
->name
);
1213 symbol_name
= symbol
->name
?: "<anonymous>";
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
);
1218 if (field
->field_type
== VARLINK_ENUM_VALUE
) {
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1245 if (field
->field_type
== VARLINK_NAMED_TYPE
) {
1246 const VarlinkSymbol
*found
;
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
);
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
);
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
);
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
);
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. */
1262 r
= varlink_idl_symbol_consistent(interface
, field
->symbol
, level
);
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
);
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
);
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
);
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
);
1286 static bool varlink_symbol_is_empty(const VarlinkSymbol
*symbol
) {
1289 return symbol
->fields
[0].field_type
== _VARLINK_FIELD_TYPE_END_MARKER
;
1292 static int varlink_idl_symbol_consistent(
1293 const VarlinkInterface
*interface
,
1294 const VarlinkSymbol
*symbol
,
1297 _cleanup_(set_freep
) Set
*input_set
= NULL
, *output_set
= NULL
;
1298 const char *symbol_name
;
1304 symbol_name
= symbol
->name
?: "<anonymous>";
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
);
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
);
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 */
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
);
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
);
1321 if (set_ensure_put(name_set
, &string_hash_ops
, field
->name
) < 0)
1324 r
= varlink_idl_field_consistent(interface
, symbol
, field
, level
);
1332 int varlink_idl_consistent(const VarlinkInterface
*interface
, int level
) {
1333 _cleanup_(set_freep
) Set
*name_set
= NULL
;
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
);
1341 for (const VarlinkSymbol
*const *symbol
= interface
->symbols
; *symbol
; symbol
++) {
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
));
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
);
1349 if (set_ensure_put(&name_set
, &string_hash_ops
, (*symbol
)->name
) < 0)
1352 r
= varlink_idl_symbol_consistent(interface
, *symbol
, level
);
1360 static int varlink_idl_validate_symbol(const VarlinkSymbol
*symbol
, JsonVariant
*v
, VarlinkFieldDirection direction
, const char **bad_field
);
1362 static int varlink_idl_validate_field_element_type(const VarlinkField
*field
, JsonVariant
*v
) {
1365 switch (field
->field_type
) {
1367 case VARLINK_STRUCT
:
1369 case VARLINK_NAMED_TYPE
:
1370 return varlink_idl_validate_symbol(field
->symbol
, v
, VARLINK_REGULAR
, NULL
);
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
));
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
));
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
));
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
));
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
));
1403 assert_not_reached();
1409 static int varlink_idl_validate_field(const VarlinkField
*field
, JsonVariant
*v
) {
1414 if (!v
|| json_variant_is_null(v
)) {
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
));
1419 } else if (FLAGS_SET(field
->field_flags
, VARLINK_ARRAY
)) {
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
));
1425 JSON_VARIANT_ARRAY_FOREACH(i
, v
) {
1426 r
= varlink_idl_validate_field_element_type(field
, i
);
1431 } else if (FLAGS_SET(field
->field_flags
, VARLINK_MAP
)) {
1432 _unused_
const char *k
;
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
));
1438 JSON_VARIANT_OBJECT_FOREACH(k
, e
, v
) {
1439 r
= varlink_idl_validate_field_element_type(field
, e
);
1445 r
= varlink_idl_validate_field_element_type(field
, v
);
1453 static int varlink_idl_validate_symbol(const VarlinkSymbol
*symbol
, JsonVariant
*v
, VarlinkFieldDirection direction
, const char **bad_field
) {
1461 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
), "Null object passed, refusing.");
1464 switch (symbol
->symbol_type
) {
1466 case VARLINK_ENUM_TYPE
: {
1470 if (!json_variant_is_string(v
)) {
1472 *bad_field
= symbol
->name
;
1473 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
), "Passed non-string to enum field '%s', refusing.", strna(symbol
->name
));
1476 assert_se(s
= json_variant_string(v
));
1478 for (const VarlinkField
*field
= symbol
->fields
; field
->field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; field
++) {
1480 assert(field
->field_type
== VARLINK_ENUM_VALUE
);
1482 if (streq_ptr(field
->name
, s
)) {
1491 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
), "Passed unrecognized string '%s' to enum field '%s', refusing.", s
, strna(symbol
->name
));
1497 case VARLINK_STRUCT_TYPE
:
1498 case VARLINK_METHOD
:
1499 case VARLINK_ERROR
: {
1500 if (!json_variant_is_object(v
)) {
1502 *bad_field
= symbol
->name
;
1503 return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
), "Passed non-object to field '%s', refusing.", strna(symbol
->name
));
1506 for (const VarlinkField
*field
= symbol
->fields
; field
->field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; field
++) {
1508 if (field
->field_direction
!= direction
)
1511 r
= varlink_idl_validate_field(field
, json_variant_by_key(v
, field
->name
));
1514 *bad_field
= field
->name
;
1519 _unused_ JsonVariant
*e
;
1521 JSON_VARIANT_OBJECT_FOREACH(name
, e
, v
) {
1522 if (!varlink_idl_find_field(symbol
, name
)) {
1525 return log_debug_errno(SYNTHETIC_ERRNO(EBUSY
), "Field '%s' not defined for object, refusing.", name
);
1533 assert_not_reached();
1536 return 1; /* validated */
1539 static 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
));
1543 return 0; /* Can't validate */
1544 if (method
->symbol_type
!= VARLINK_METHOD
)
1547 return varlink_idl_validate_symbol(method
, v
, direction
, bad_field
);
1550 int 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
);
1554 int 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
);
1558 int varlink_idl_validate_error(const VarlinkSymbol
*error
, JsonVariant
*v
, const char **bad_field
) {
1560 return 0; /* Can't validate */
1561 if (error
->symbol_type
!= VARLINK_ERROR
)
1564 return varlink_idl_validate_symbol(error
, v
, VARLINK_REGULAR
, bad_field
);
1567 const VarlinkSymbol
* varlink_idl_find_symbol(
1568 const VarlinkInterface
*interface
,
1569 VarlinkSymbolType type
,
1573 assert(type
< _VARLINK_SYMBOL_TYPE_MAX
);
1578 for (const VarlinkSymbol
*const*symbol
= interface
->symbols
; *symbol
; symbol
++) {
1579 if (type
>= 0 && (*symbol
)->symbol_type
!= type
)
1582 if (streq_ptr((*symbol
)->name
, name
))
1589 const VarlinkField
* varlink_idl_find_field(
1590 const VarlinkSymbol
*symbol
,
1598 for (const VarlinkField
*field
= symbol
->fields
; field
->field_type
!= _VARLINK_FIELD_TYPE_END_MARKER
; field
++)
1599 if (streq_ptr(field
->name
, name
))