1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "alloc-util.h"
6 #include "busctl-introspect.h"
8 #include "string-util.h"
12 #define NODE_DEPTH_MAX 16
14 typedef struct Context
{
15 const XMLIntrospectOps
*ops
;
19 uint64_t interface_flags
;
22 char *member_signature
;
24 uint64_t member_flags
;
31 static void context_reset_member(Context
*c
) {
33 free(c
->member_signature
);
34 free(c
->member_result
);
36 c
->member_name
= c
->member_signature
= c
->member_result
= NULL
;
38 c
->member_writable
= false;
41 static void context_reset_interface(Context
*c
) {
42 c
->interface_name
= mfree(c
->interface_name
);
43 c
->interface_flags
= 0;
45 context_reset_member(c
);
48 static int parse_xml_annotation(Context
*context
, uint64_t *flags
) {
54 } state
= STATE_ANNOTATION
;
56 _cleanup_free_
char *field
= NULL
, *value
= NULL
;
61 _cleanup_free_
char *name
= NULL
;
65 t
= xml_tokenize(&context
->current
, &name
, &context
->xml_state
, NULL
);
67 log_error("XML parse error.");
72 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
73 "Premature end of XML data.");
77 case STATE_ANNOTATION
:
79 if (t
== XML_ATTRIBUTE_NAME
) {
81 if (streq_ptr(name
, "name"))
84 else if (streq_ptr(name
, "value"))
88 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
89 "Unexpected <annotation> attribute %s.",
92 } else if (t
== XML_TAG_CLOSE_EMPTY
||
93 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "annotation"))) {
96 if (streq_ptr(field
, "org.freedesktop.DBus.Deprecated")) {
98 if (streq_ptr(value
, "true"))
99 *flags
|= SD_BUS_VTABLE_DEPRECATED
;
101 } else if (streq_ptr(field
, "org.freedesktop.DBus.Method.NoReply")) {
103 if (streq_ptr(value
, "true"))
104 *flags
|= SD_BUS_VTABLE_METHOD_NO_REPLY
;
106 } else if (streq_ptr(field
, "org.freedesktop.DBus.Property.EmitsChangedSignal")) {
108 if (streq_ptr(value
, "const"))
109 *flags
= (*flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
)) | SD_BUS_VTABLE_PROPERTY_CONST
;
110 else if (streq_ptr(value
, "invalidates"))
111 *flags
= (*flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_CONST
)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
;
112 else if (streq_ptr(value
, "false"))
113 *flags
= *flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
);
119 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
120 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
121 "Unexpected token in <annotation>. (1)");
127 if (t
== XML_ATTRIBUTE_VALUE
) {
128 free_and_replace(field
, name
);
130 state
= STATE_ANNOTATION
;
132 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
133 "Unexpected token in <annotation>. (2)");
139 if (t
== XML_ATTRIBUTE_VALUE
) {
140 free_and_replace(value
, name
);
142 state
= STATE_ANNOTATION
;
144 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
145 "Unexpected token in <annotation>. (3)");
150 assert_not_reached("Bad state");
155 static int parse_xml_node(Context
*context
, const char *prefix
, unsigned n_depth
) {
161 STATE_INTERFACE_NAME
,
165 STATE_METHOD_ARG_NAME
,
166 STATE_METHOD_ARG_TYPE
,
167 STATE_METHOD_ARG_DIRECTION
,
171 STATE_SIGNAL_ARG_NAME
,
172 STATE_SIGNAL_ARG_TYPE
,
173 STATE_SIGNAL_ARG_DIRECTION
,
177 STATE_PROPERTY_ACCESS
,
178 } state
= STATE_NODE
;
180 _cleanup_free_
char *node_path
= NULL
, *argument_type
= NULL
, *argument_direction
= NULL
;
181 const char *np
= prefix
;
187 if (n_depth
> NODE_DEPTH_MAX
)
188 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "<node> depth too high.");
191 _cleanup_free_
char *name
= NULL
;
194 t
= xml_tokenize(&context
->current
, &name
, &context
->xml_state
, NULL
);
196 log_error("XML parse error.");
201 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Premature end of XML data.");
206 if (t
== XML_ATTRIBUTE_NAME
) {
208 if (streq_ptr(name
, "name"))
209 state
= STATE_NODE_NAME
;
211 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
212 "Unexpected <node> attribute %s.", name
);
214 } else if (t
== XML_TAG_OPEN
) {
216 if (streq_ptr(name
, "interface"))
217 state
= STATE_INTERFACE
;
218 else if (streq_ptr(name
, "node")) {
220 r
= parse_xml_node(context
, np
, n_depth
+1);
224 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
225 "Unexpected <node> tag %s.", name
);
227 } else if (t
== XML_TAG_CLOSE_EMPTY
||
228 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "node"))) {
230 if (context
->ops
->on_path
) {
231 r
= context
->ops
->on_path(node_path
? node_path
: np
, context
->userdata
);
238 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
239 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
240 "Unexpected token in <node>. (1)");
244 case STATE_NODE_NAME
:
246 if (t
== XML_ATTRIBUTE_VALUE
) {
251 node_path
= TAKE_PTR(name
);
254 node_path
= path_join(prefix
, name
);
262 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
263 "Unexpected token in <node>. (2)");
267 case STATE_INTERFACE
:
269 if (t
== XML_ATTRIBUTE_NAME
) {
270 if (streq_ptr(name
, "name"))
271 state
= STATE_INTERFACE_NAME
;
273 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
274 "Unexpected <interface> attribute %s.",
277 } else if (t
== XML_TAG_OPEN
) {
278 if (streq_ptr(name
, "method"))
279 state
= STATE_METHOD
;
280 else if (streq_ptr(name
, "signal"))
281 state
= STATE_SIGNAL
;
282 else if (streq_ptr(name
, "property")) {
283 context
->member_flags
|= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
;
284 state
= STATE_PROPERTY
;
285 } else if (streq_ptr(name
, "annotation")) {
286 r
= parse_xml_annotation(context
, &context
->interface_flags
);
290 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
291 "Unexpected <interface> tag %s.", name
);
292 } else if (t
== XML_TAG_CLOSE_EMPTY
||
293 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "interface"))) {
296 if (context
->ops
->on_interface
) {
297 r
= context
->ops
->on_interface(context
->interface_name
, context
->interface_flags
, context
->userdata
);
302 context_reset_interface(context
);
307 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
308 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
309 "Unexpected token in <interface>. (1)");
313 case STATE_INTERFACE_NAME
:
315 if (t
== XML_ATTRIBUTE_VALUE
) {
317 free_and_replace(context
->interface_name
, name
);
319 state
= STATE_INTERFACE
;
321 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
322 "Unexpected token in <interface>. (2)");
328 if (t
== XML_ATTRIBUTE_NAME
) {
329 if (streq_ptr(name
, "name"))
330 state
= STATE_METHOD_NAME
;
332 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
333 "Unexpected <method> attribute %s",
335 } else if (t
== XML_TAG_OPEN
) {
336 if (streq_ptr(name
, "arg"))
337 state
= STATE_METHOD_ARG
;
338 else if (streq_ptr(name
, "annotation")) {
339 r
= parse_xml_annotation(context
, &context
->member_flags
);
343 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
344 "Unexpected <method> tag %s.",
346 } else if (t
== XML_TAG_CLOSE_EMPTY
||
347 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "method"))) {
350 if (context
->ops
->on_method
) {
351 r
= context
->ops
->on_method(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_result
, context
->member_flags
, context
->userdata
);
356 context_reset_member(context
);
359 state
= STATE_INTERFACE
;
361 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
362 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
363 "Unexpected token in <method> (1).");
367 case STATE_METHOD_NAME
:
369 if (t
== XML_ATTRIBUTE_VALUE
) {
371 free_and_replace(context
->member_name
, name
);
373 state
= STATE_METHOD
;
375 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
376 "Unexpected token in <method> (2).");
380 case STATE_METHOD_ARG
:
382 if (t
== XML_ATTRIBUTE_NAME
) {
383 if (streq_ptr(name
, "name"))
384 state
= STATE_METHOD_ARG_NAME
;
385 else if (streq_ptr(name
, "type"))
386 state
= STATE_METHOD_ARG_TYPE
;
387 else if (streq_ptr(name
, "direction"))
388 state
= STATE_METHOD_ARG_DIRECTION
;
390 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
391 "Unexpected method <arg> attribute %s.",
393 } else if (t
== XML_TAG_OPEN
) {
394 if (streq_ptr(name
, "annotation")) {
395 r
= parse_xml_annotation(context
, NULL
);
399 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
400 "Unexpected method <arg> tag %s.",
402 } else if (t
== XML_TAG_CLOSE_EMPTY
||
403 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "arg"))) {
408 if (!argument_direction
|| streq(argument_direction
, "in")) {
409 if (!strextend(&context
->member_signature
, argument_type
, NULL
))
411 } else if (streq(argument_direction
, "out")) {
412 if (!strextend(&context
->member_result
, argument_type
, NULL
))
415 log_error("Unexpected method <arg> direction value '%s'.", argument_direction
);
418 argument_type
= mfree(argument_type
);
419 argument_direction
= mfree(argument_direction
);
422 state
= STATE_METHOD
;
423 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
424 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
425 "Unexpected token in method <arg>. (1)");
429 case STATE_METHOD_ARG_NAME
:
431 if (t
== XML_ATTRIBUTE_VALUE
)
432 state
= STATE_METHOD_ARG
;
434 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
435 "Unexpected token in method <arg>. (2)");
439 case STATE_METHOD_ARG_TYPE
:
441 if (t
== XML_ATTRIBUTE_VALUE
) {
442 free_and_replace(argument_type
, name
);
444 state
= STATE_METHOD_ARG
;
446 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
447 "Unexpected token in method <arg>. (3)");
451 case STATE_METHOD_ARG_DIRECTION
:
453 if (t
== XML_ATTRIBUTE_VALUE
) {
454 free_and_replace(argument_direction
, name
);
456 state
= STATE_METHOD_ARG
;
458 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
459 "Unexpected token in method <arg>. (4)");
465 if (t
== XML_ATTRIBUTE_NAME
) {
466 if (streq_ptr(name
, "name"))
467 state
= STATE_SIGNAL_NAME
;
469 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
470 "Unexpected <signal> attribute %s.",
472 } else if (t
== XML_TAG_OPEN
) {
473 if (streq_ptr(name
, "arg"))
474 state
= STATE_SIGNAL_ARG
;
475 else if (streq_ptr(name
, "annotation")) {
476 r
= parse_xml_annotation(context
, &context
->member_flags
);
480 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
481 "Unexpected <signal> tag %s.",
483 } else if (t
== XML_TAG_CLOSE_EMPTY
||
484 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "signal"))) {
487 if (context
->ops
->on_signal
) {
488 r
= context
->ops
->on_signal(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_flags
, context
->userdata
);
493 context_reset_member(context
);
496 state
= STATE_INTERFACE
;
498 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
499 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
500 "Unexpected token in <signal>. (1)");
504 case STATE_SIGNAL_NAME
:
506 if (t
== XML_ATTRIBUTE_VALUE
) {
508 free_and_replace(context
->member_name
, name
);
510 state
= STATE_SIGNAL
;
512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
513 "Unexpected token in <signal>. (2)");
517 case STATE_SIGNAL_ARG
:
519 if (t
== XML_ATTRIBUTE_NAME
) {
520 if (streq_ptr(name
, "name"))
521 state
= STATE_SIGNAL_ARG_NAME
;
522 else if (streq_ptr(name
, "type"))
523 state
= STATE_SIGNAL_ARG_TYPE
;
524 else if (streq_ptr(name
, "direction"))
525 state
= STATE_SIGNAL_ARG_DIRECTION
;
527 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
528 "Unexpected signal <arg> attribute %s.",
530 } else if (t
== XML_TAG_OPEN
) {
531 if (streq_ptr(name
, "annotation")) {
532 r
= parse_xml_annotation(context
, NULL
);
536 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
537 "Unexpected signal <arg> tag %s.",
539 } else if (t
== XML_TAG_CLOSE_EMPTY
||
540 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "arg"))) {
543 if (!argument_direction
|| streq(argument_direction
, "out")) {
544 if (!strextend(&context
->member_signature
, argument_type
, NULL
))
547 log_error("Unexpected signal <arg> direction value '%s'.", argument_direction
);
549 argument_type
= mfree(argument_type
);
552 state
= STATE_SIGNAL
;
553 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
554 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
555 "Unexpected token in signal <arg> (1).");
559 case STATE_SIGNAL_ARG_NAME
:
561 if (t
== XML_ATTRIBUTE_VALUE
)
562 state
= STATE_SIGNAL_ARG
;
564 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
565 "Unexpected token in signal <arg> (2).");
569 case STATE_SIGNAL_ARG_TYPE
:
571 if (t
== XML_ATTRIBUTE_VALUE
) {
572 free_and_replace(argument_type
, name
);
574 state
= STATE_SIGNAL_ARG
;
576 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
577 "Unexpected token in signal <arg> (3).");
581 case STATE_SIGNAL_ARG_DIRECTION
:
583 if (t
== XML_ATTRIBUTE_VALUE
) {
584 free_and_replace(argument_direction
, name
);
586 state
= STATE_SIGNAL_ARG
;
588 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
589 "Unexpected token in signal <arg>. (4)");
595 if (t
== XML_ATTRIBUTE_NAME
) {
596 if (streq_ptr(name
, "name"))
597 state
= STATE_PROPERTY_NAME
;
598 else if (streq_ptr(name
, "type"))
599 state
= STATE_PROPERTY_TYPE
;
600 else if (streq_ptr(name
, "access"))
601 state
= STATE_PROPERTY_ACCESS
;
603 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
604 "Unexpected <property> attribute %s.",
606 } else if (t
== XML_TAG_OPEN
) {
608 if (streq_ptr(name
, "annotation")) {
609 r
= parse_xml_annotation(context
, &context
->member_flags
);
613 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
614 "Unexpected <property> tag %s.",
617 } else if (t
== XML_TAG_CLOSE_EMPTY
||
618 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "property"))) {
621 if (context
->ops
->on_property
) {
622 r
= context
->ops
->on_property(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_writable
, context
->member_flags
, context
->userdata
);
627 context_reset_member(context
);
630 state
= STATE_INTERFACE
;
632 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
))
633 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
634 "Unexpected token in <property>. (1)");
638 case STATE_PROPERTY_NAME
:
640 if (t
== XML_ATTRIBUTE_VALUE
) {
642 free_and_replace(context
->member_name
, name
);
644 state
= STATE_PROPERTY
;
646 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
647 "Unexpected token in <property>. (2)");
651 case STATE_PROPERTY_TYPE
:
653 if (t
== XML_ATTRIBUTE_VALUE
) {
655 free_and_replace(context
->member_signature
, name
);
657 state
= STATE_PROPERTY
;
659 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
660 "Unexpected token in <property>. (3)");
664 case STATE_PROPERTY_ACCESS
:
666 if (t
== XML_ATTRIBUTE_VALUE
) {
668 if (streq(name
, "readwrite") || streq(name
, "write"))
669 context
->member_writable
= true;
671 state
= STATE_PROPERTY
;
673 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
674 "Unexpected token in <property>. (4)");
681 int parse_xml_introspect(const char *prefix
, const char *xml
, const XMLIntrospectOps
*ops
, void *userdata
) {
684 .userdata
= userdata
,
695 _cleanup_free_
char *name
= NULL
;
697 r
= xml_tokenize(&context
.current
, &name
, &context
.xml_state
, NULL
);
699 log_error("XML parse error");
708 if (r
== XML_TAG_OPEN
) {
710 if (streq(name
, "node")) {
711 r
= parse_xml_node(&context
, prefix
, 0);
715 log_error("Unexpected tag '%s' in introspection data.", name
);
719 } else if (r
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
720 log_error("Unexpected token.");
727 context_reset_interface(&context
);