1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "alloc-util.h"
6 #include "busctl-introspect.h"
7 #include "string-util.h"
11 #define NODE_DEPTH_MAX 16
13 typedef struct Context
{
14 const XMLIntrospectOps
*ops
;
18 uint64_t interface_flags
;
21 char *member_signature
;
23 uint64_t member_flags
;
30 static void context_reset_member(Context
*c
) {
32 free(c
->member_signature
);
33 free(c
->member_result
);
35 c
->member_name
= c
->member_signature
= c
->member_result
= NULL
;
37 c
->member_writable
= false;
40 static void context_reset_interface(Context
*c
) {
41 c
->interface_name
= mfree(c
->interface_name
);
42 c
->interface_flags
= 0;
44 context_reset_member(c
);
47 static int parse_xml_annotation(Context
*context
, uint64_t *flags
) {
53 } state
= STATE_ANNOTATION
;
55 _cleanup_free_
char *field
= NULL
, *value
= NULL
;
60 _cleanup_free_
char *name
= NULL
;
64 t
= xml_tokenize(&context
->current
, &name
, &context
->xml_state
, NULL
);
66 log_error("XML parse error.");
71 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
72 "Premature end of XML data.");
76 case STATE_ANNOTATION
:
78 if (t
== XML_ATTRIBUTE_NAME
) {
80 if (streq_ptr(name
, "name"))
83 else if (streq_ptr(name
, "value"))
87 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
88 "Unexpected <annotation> attribute %s.",
91 } else if (t
== XML_TAG_CLOSE_EMPTY
||
92 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "annotation"))) {
95 if (streq_ptr(field
, "org.freedesktop.DBus.Deprecated")) {
97 if (streq_ptr(value
, "true"))
98 *flags
|= SD_BUS_VTABLE_DEPRECATED
;
100 } else if (streq_ptr(field
, "org.freedesktop.DBus.Method.NoReply")) {
102 if (streq_ptr(value
, "true"))
103 *flags
|= SD_BUS_VTABLE_METHOD_NO_REPLY
;
105 } else if (streq_ptr(field
, "org.freedesktop.DBus.Property.EmitsChangedSignal")) {
107 if (streq_ptr(value
, "const"))
108 *flags
= (*flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
)) | SD_BUS_VTABLE_PROPERTY_CONST
;
109 else if (streq_ptr(value
, "invalidates"))
110 *flags
= (*flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_CONST
)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
;
111 else if (streq_ptr(value
, "false"))
112 *flags
= *flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
);
118 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
119 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
120 "Unexpected token in <annotation>. (1)");
126 if (t
== XML_ATTRIBUTE_VALUE
) {
127 free_and_replace(field
, name
);
129 state
= STATE_ANNOTATION
;
131 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
132 "Unexpected token in <annotation>. (2)");
138 if (t
== XML_ATTRIBUTE_VALUE
) {
139 free_and_replace(value
, name
);
141 state
= STATE_ANNOTATION
;
143 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
144 "Unexpected token in <annotation>. (3)");
149 assert_not_reached("Bad state");
154 static int parse_xml_node(Context
*context
, const char *prefix
, unsigned n_depth
) {
160 STATE_INTERFACE_NAME
,
164 STATE_METHOD_ARG_NAME
,
165 STATE_METHOD_ARG_TYPE
,
166 STATE_METHOD_ARG_DIRECTION
,
170 STATE_SIGNAL_ARG_NAME
,
171 STATE_SIGNAL_ARG_TYPE
,
172 STATE_SIGNAL_ARG_DIRECTION
,
176 STATE_PROPERTY_ACCESS
,
177 } state
= STATE_NODE
;
179 _cleanup_free_
char *node_path
= NULL
, *argument_type
= NULL
, *argument_direction
= NULL
;
180 const char *np
= prefix
;
186 if (n_depth
> NODE_DEPTH_MAX
)
187 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "<node> depth too high.");
190 _cleanup_free_
char *name
= NULL
;
193 t
= xml_tokenize(&context
->current
, &name
, &context
->xml_state
, NULL
);
195 log_error("XML parse error.");
200 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Premature end of XML data.");
205 if (t
== XML_ATTRIBUTE_NAME
) {
207 if (streq_ptr(name
, "name"))
208 state
= STATE_NODE_NAME
;
210 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
211 "Unexpected <node> attribute %s.", name
);
213 } else if (t
== XML_TAG_OPEN
) {
215 if (streq_ptr(name
, "interface"))
216 state
= STATE_INTERFACE
;
217 else if (streq_ptr(name
, "node")) {
219 r
= parse_xml_node(context
, np
, n_depth
+1);
223 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
224 "Unexpected <node> tag %s.", name
);
226 } else if (t
== XML_TAG_CLOSE_EMPTY
||
227 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "node"))) {
229 if (context
->ops
->on_path
) {
230 r
= context
->ops
->on_path(node_path
? node_path
: np
, context
->userdata
);
237 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
238 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
239 "Unexpected token in <node>. (1)");
243 case STATE_NODE_NAME
:
245 if (t
== XML_ATTRIBUTE_VALUE
) {
250 node_path
= TAKE_PTR(name
);
253 if (endswith(prefix
, "/"))
254 node_path
= strappend(prefix
, name
);
256 node_path
= strjoin(prefix
, "/", name
);
264 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
265 "Unexpected token in <node>. (2)");
269 case STATE_INTERFACE
:
271 if (t
== XML_ATTRIBUTE_NAME
) {
272 if (streq_ptr(name
, "name"))
273 state
= STATE_INTERFACE_NAME
;
275 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
276 "Unexpected <interface> attribute %s.",
279 } else if (t
== XML_TAG_OPEN
) {
280 if (streq_ptr(name
, "method"))
281 state
= STATE_METHOD
;
282 else if (streq_ptr(name
, "signal"))
283 state
= STATE_SIGNAL
;
284 else if (streq_ptr(name
, "property")) {
285 context
->member_flags
|= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
;
286 state
= STATE_PROPERTY
;
287 } else if (streq_ptr(name
, "annotation")) {
288 r
= parse_xml_annotation(context
, &context
->interface_flags
);
292 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
293 "Unexpected <interface> tag %s.", name
);
294 } else if (t
== XML_TAG_CLOSE_EMPTY
||
295 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "interface"))) {
298 if (context
->ops
->on_interface
) {
299 r
= context
->ops
->on_interface(context
->interface_name
, context
->interface_flags
, context
->userdata
);
304 context_reset_interface(context
);
309 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
310 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
311 "Unexpected token in <interface>. (1)");
315 case STATE_INTERFACE_NAME
:
317 if (t
== XML_ATTRIBUTE_VALUE
) {
319 free_and_replace(context
->interface_name
, name
);
321 state
= STATE_INTERFACE
;
323 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
324 "Unexpected token in <interface>. (2)");
330 if (t
== XML_ATTRIBUTE_NAME
) {
331 if (streq_ptr(name
, "name"))
332 state
= STATE_METHOD_NAME
;
334 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
335 "Unexpected <method> attribute %s",
337 } else if (t
== XML_TAG_OPEN
) {
338 if (streq_ptr(name
, "arg"))
339 state
= STATE_METHOD_ARG
;
340 else if (streq_ptr(name
, "annotation")) {
341 r
= parse_xml_annotation(context
, &context
->member_flags
);
345 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
346 "Unexpected <method> tag %s.",
348 } else if (t
== XML_TAG_CLOSE_EMPTY
||
349 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "method"))) {
352 if (context
->ops
->on_method
) {
353 r
= context
->ops
->on_method(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_result
, context
->member_flags
, context
->userdata
);
358 context_reset_member(context
);
361 state
= STATE_INTERFACE
;
363 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
364 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
365 "Unexpected token in <method> (1).");
369 case STATE_METHOD_NAME
:
371 if (t
== XML_ATTRIBUTE_VALUE
) {
373 free_and_replace(context
->member_name
, name
);
375 state
= STATE_METHOD
;
377 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
378 "Unexpected token in <method> (2).");
382 case STATE_METHOD_ARG
:
384 if (t
== XML_ATTRIBUTE_NAME
) {
385 if (streq_ptr(name
, "name"))
386 state
= STATE_METHOD_ARG_NAME
;
387 else if (streq_ptr(name
, "type"))
388 state
= STATE_METHOD_ARG_TYPE
;
389 else if (streq_ptr(name
, "direction"))
390 state
= STATE_METHOD_ARG_DIRECTION
;
392 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
393 "Unexpected method <arg> attribute %s.",
395 } else if (t
== XML_TAG_OPEN
) {
396 if (streq_ptr(name
, "annotation")) {
397 r
= parse_xml_annotation(context
, NULL
);
401 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
402 "Unexpected method <arg> tag %s.",
404 } else if (t
== XML_TAG_CLOSE_EMPTY
||
405 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "arg"))) {
410 if (!argument_direction
|| streq(argument_direction
, "in")) {
411 if (!strextend(&context
->member_signature
, argument_type
, NULL
))
413 } else if (streq(argument_direction
, "out")) {
414 if (!strextend(&context
->member_result
, argument_type
, NULL
))
417 log_error("Unexpected method <arg> direction value '%s'.", argument_direction
);
420 argument_type
= mfree(argument_type
);
421 argument_direction
= mfree(argument_direction
);
424 state
= STATE_METHOD
;
425 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
426 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
427 "Unexpected token in method <arg>. (1)");
431 case STATE_METHOD_ARG_NAME
:
433 if (t
== XML_ATTRIBUTE_VALUE
)
434 state
= STATE_METHOD_ARG
;
436 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
437 "Unexpected token in method <arg>. (2)");
441 case STATE_METHOD_ARG_TYPE
:
443 if (t
== XML_ATTRIBUTE_VALUE
) {
444 free_and_replace(argument_type
, name
);
446 state
= STATE_METHOD_ARG
;
448 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
449 "Unexpected token in method <arg>. (3)");
453 case STATE_METHOD_ARG_DIRECTION
:
455 if (t
== XML_ATTRIBUTE_VALUE
) {
456 free_and_replace(argument_direction
, name
);
458 state
= STATE_METHOD_ARG
;
460 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
461 "Unexpected token in method <arg>. (4)");
467 if (t
== XML_ATTRIBUTE_NAME
) {
468 if (streq_ptr(name
, "name"))
469 state
= STATE_SIGNAL_NAME
;
471 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
472 "Unexpected <signal> attribute %s.",
474 } else if (t
== XML_TAG_OPEN
) {
475 if (streq_ptr(name
, "arg"))
476 state
= STATE_SIGNAL_ARG
;
477 else if (streq_ptr(name
, "annotation")) {
478 r
= parse_xml_annotation(context
, &context
->member_flags
);
482 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
483 "Unexpected <signal> tag %s.",
485 } else if (t
== XML_TAG_CLOSE_EMPTY
||
486 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "signal"))) {
489 if (context
->ops
->on_signal
) {
490 r
= context
->ops
->on_signal(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_flags
, context
->userdata
);
495 context_reset_member(context
);
498 state
= STATE_INTERFACE
;
500 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
501 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
502 "Unexpected token in <signal>. (1)");
506 case STATE_SIGNAL_NAME
:
508 if (t
== XML_ATTRIBUTE_VALUE
) {
510 free_and_replace(context
->member_name
, name
);
512 state
= STATE_SIGNAL
;
514 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
515 "Unexpected token in <signal>. (2)");
519 case STATE_SIGNAL_ARG
:
521 if (t
== XML_ATTRIBUTE_NAME
) {
522 if (streq_ptr(name
, "name"))
523 state
= STATE_SIGNAL_ARG_NAME
;
524 else if (streq_ptr(name
, "type"))
525 state
= STATE_SIGNAL_ARG_TYPE
;
526 else if (streq_ptr(name
, "direction"))
527 state
= STATE_SIGNAL_ARG_DIRECTION
;
529 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
530 "Unexpected signal <arg> attribute %s.",
532 } else if (t
== XML_TAG_OPEN
) {
533 if (streq_ptr(name
, "annotation")) {
534 r
= parse_xml_annotation(context
, NULL
);
538 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
539 "Unexpected signal <arg> tag %s.",
541 } else if (t
== XML_TAG_CLOSE_EMPTY
||
542 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "arg"))) {
545 if (!argument_direction
|| streq(argument_direction
, "out")) {
546 if (!strextend(&context
->member_signature
, argument_type
, NULL
))
549 log_error("Unexpected signal <arg> direction value '%s'.", argument_direction
);
551 argument_type
= mfree(argument_type
);
554 state
= STATE_SIGNAL
;
555 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
556 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
557 "Unexpected token in signal <arg> (1).");
561 case STATE_SIGNAL_ARG_NAME
:
563 if (t
== XML_ATTRIBUTE_VALUE
)
564 state
= STATE_SIGNAL_ARG
;
566 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
567 "Unexpected token in signal <arg> (2).");
571 case STATE_SIGNAL_ARG_TYPE
:
573 if (t
== XML_ATTRIBUTE_VALUE
) {
574 free_and_replace(argument_type
, name
);
576 state
= STATE_SIGNAL_ARG
;
578 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
579 "Unexpected token in signal <arg> (3).");
583 case STATE_SIGNAL_ARG_DIRECTION
:
585 if (t
== XML_ATTRIBUTE_VALUE
) {
586 free_and_replace(argument_direction
, name
);
588 state
= STATE_SIGNAL_ARG
;
590 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
591 "Unexpected token in signal <arg>. (4)");
597 if (t
== XML_ATTRIBUTE_NAME
) {
598 if (streq_ptr(name
, "name"))
599 state
= STATE_PROPERTY_NAME
;
600 else if (streq_ptr(name
, "type"))
601 state
= STATE_PROPERTY_TYPE
;
602 else if (streq_ptr(name
, "access"))
603 state
= STATE_PROPERTY_ACCESS
;
605 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
606 "Unexpected <property> attribute %s.",
608 } else if (t
== XML_TAG_OPEN
) {
610 if (streq_ptr(name
, "annotation")) {
611 r
= parse_xml_annotation(context
, &context
->member_flags
);
615 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
616 "Unexpected <property> tag %s.",
619 } else if (t
== XML_TAG_CLOSE_EMPTY
||
620 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "property"))) {
623 if (context
->ops
->on_property
) {
624 r
= context
->ops
->on_property(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_writable
, context
->member_flags
, context
->userdata
);
629 context_reset_member(context
);
632 state
= STATE_INTERFACE
;
634 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
635 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
636 "Unexpected token in <property>. (1)");
640 case STATE_PROPERTY_NAME
:
642 if (t
== XML_ATTRIBUTE_VALUE
) {
644 free_and_replace(context
->member_name
, name
);
646 state
= STATE_PROPERTY
;
648 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
649 "Unexpected token in <property>. (2)");
653 case STATE_PROPERTY_TYPE
:
655 if (t
== XML_ATTRIBUTE_VALUE
) {
657 free_and_replace(context
->member_signature
, name
);
659 state
= STATE_PROPERTY
;
661 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
662 "Unexpected token in <property>. (3)");
666 case STATE_PROPERTY_ACCESS
:
668 if (t
== XML_ATTRIBUTE_VALUE
) {
670 if (streq(name
, "readwrite") || streq(name
, "write"))
671 context
->member_writable
= true;
673 state
= STATE_PROPERTY
;
675 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
676 "Unexpected token in <property>. (4)");
683 int parse_xml_introspect(const char *prefix
, const char *xml
, const XMLIntrospectOps
*ops
, void *userdata
) {
686 .userdata
= userdata
,
697 _cleanup_free_
char *name
= NULL
;
699 r
= xml_tokenize(&context
.current
, &name
, &context
.xml_state
, NULL
);
701 log_error("XML parse error");
710 if (r
== XML_TAG_OPEN
) {
712 if (streq(name
, "node")) {
713 r
= parse_xml_node(&context
, prefix
, 0);
717 log_error("Unexpected tag '%s' in introspection data.", name
);
721 } else if (r
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
722 log_error("Unexpected token.");
729 context_reset_interface(&context
);