1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "sd-bus-vtable.h"
26 #include "busctl-introspect.h"
28 #define NODE_DEPTH_MAX 16
30 typedef struct Context
{
31 const XMLIntrospectOps
*ops
;
35 uint64_t interface_flags
;
38 char *member_signature
;
40 uint64_t member_flags
;
47 static void context_reset_member(Context
*c
) {
49 free(c
->member_signature
);
50 free(c
->member_result
);
52 c
->member_name
= c
->member_signature
= c
->member_result
= NULL
;
54 c
->member_writable
= false;
57 static void context_reset_interface(Context
*c
) {
58 c
->interface_name
= mfree(c
->interface_name
);
59 c
->interface_flags
= 0;
61 context_reset_member(c
);
64 static int parse_xml_annotation(Context
*context
, uint64_t *flags
) {
70 } state
= STATE_ANNOTATION
;
72 _cleanup_free_
char *field
= NULL
, *value
= NULL
;
77 _cleanup_free_
char *name
= NULL
;
81 t
= xml_tokenize(&context
->current
, &name
, &context
->xml_state
, NULL
);
83 log_error("XML parse error.");
88 log_error("Premature end of XML data.");
94 case STATE_ANNOTATION
:
96 if (t
== XML_ATTRIBUTE_NAME
) {
98 if (streq_ptr(name
, "name"))
101 else if (streq_ptr(name
, "value"))
105 log_error("Unexpected <annotation> attribute %s.", name
);
109 } else if (t
== XML_TAG_CLOSE_EMPTY
||
110 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "annotation"))) {
113 if (streq_ptr(field
, "org.freedesktop.DBus.Deprecated")) {
115 if (streq_ptr(value
, "true"))
116 *flags
|= SD_BUS_VTABLE_DEPRECATED
;
118 } else if (streq_ptr(field
, "org.freedesktop.DBus.Method.NoReply")) {
120 if (streq_ptr(value
, "true"))
121 *flags
|= SD_BUS_VTABLE_METHOD_NO_REPLY
;
123 } else if (streq_ptr(field
, "org.freedesktop.DBus.Property.EmitsChangedSignal")) {
125 if (streq_ptr(value
, "const"))
126 *flags
= (*flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
)) | SD_BUS_VTABLE_PROPERTY_CONST
;
127 else if (streq_ptr(value
, "invalidates"))
128 *flags
= (*flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_CONST
)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
;
129 else if (streq_ptr(value
, "false"))
130 *flags
= *flags
& ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
);
136 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
137 log_error("Unexpected token in <annotation>. (1)");
145 if (t
== XML_ATTRIBUTE_VALUE
) {
150 state
= STATE_ANNOTATION
;
152 log_error("Unexpected token in <annotation>. (2)");
160 if (t
== XML_ATTRIBUTE_VALUE
) {
165 state
= STATE_ANNOTATION
;
167 log_error("Unexpected token in <annotation>. (3)");
174 assert_not_reached("Bad state");
179 static int parse_xml_node(Context
*context
, const char *prefix
, unsigned n_depth
) {
185 STATE_INTERFACE_NAME
,
189 STATE_METHOD_ARG_NAME
,
190 STATE_METHOD_ARG_TYPE
,
191 STATE_METHOD_ARG_DIRECTION
,
195 STATE_SIGNAL_ARG_NAME
,
196 STATE_SIGNAL_ARG_TYPE
,
200 STATE_PROPERTY_ACCESS
,
201 } state
= STATE_NODE
;
203 _cleanup_free_
char *node_path
= NULL
, *argument_type
= NULL
, *argument_direction
= NULL
;
204 const char *np
= prefix
;
210 if (n_depth
> NODE_DEPTH_MAX
) {
211 log_error("<node> depth too high.");
216 _cleanup_free_
char *name
= NULL
;
219 t
= xml_tokenize(&context
->current
, &name
, &context
->xml_state
, NULL
);
221 log_error("XML parse error.");
226 log_error("Premature end of XML data.");
233 if (t
== XML_ATTRIBUTE_NAME
) {
235 if (streq_ptr(name
, "name"))
236 state
= STATE_NODE_NAME
;
238 log_error("Unexpected <node> attribute %s.", name
);
242 } else if (t
== XML_TAG_OPEN
) {
244 if (streq_ptr(name
, "interface"))
245 state
= STATE_INTERFACE
;
246 else if (streq_ptr(name
, "node")) {
248 r
= parse_xml_node(context
, np
, n_depth
+1);
252 log_error("Unexpected <node> tag %s.", name
);
256 } else if (t
== XML_TAG_CLOSE_EMPTY
||
257 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "node"))) {
259 if (context
->ops
->on_path
) {
260 r
= context
->ops
->on_path(node_path
? node_path
: np
, context
->userdata
);
267 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
268 log_error("Unexpected token in <node>. (1)");
274 case STATE_NODE_NAME
:
276 if (t
== XML_ATTRIBUTE_VALUE
) {
280 if (name
[0] == '/') {
285 if (endswith(prefix
, "/"))
286 node_path
= strappend(prefix
, name
);
288 node_path
= strjoin(prefix
, "/", name
, NULL
);
296 log_error("Unexpected token in <node>. (2)");
302 case STATE_INTERFACE
:
304 if (t
== XML_ATTRIBUTE_NAME
) {
305 if (streq_ptr(name
, "name"))
306 state
= STATE_INTERFACE_NAME
;
308 log_error("Unexpected <interface> attribute %s.", name
);
312 } else if (t
== XML_TAG_OPEN
) {
313 if (streq_ptr(name
, "method"))
314 state
= STATE_METHOD
;
315 else if (streq_ptr(name
, "signal"))
316 state
= STATE_SIGNAL
;
317 else if (streq_ptr(name
, "property")) {
318 context
->member_flags
|= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
;
319 state
= STATE_PROPERTY
;
320 } else if (streq_ptr(name
, "annotation")) {
321 r
= parse_xml_annotation(context
, &context
->interface_flags
);
325 log_error("Unexpected <interface> tag %s.", name
);
328 } else if (t
== XML_TAG_CLOSE_EMPTY
||
329 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "interface"))) {
332 if (context
->ops
->on_interface
) {
333 r
= context
->ops
->on_interface(context
->interface_name
, context
->interface_flags
, context
->userdata
);
338 context_reset_interface(context
);
343 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
344 log_error("Unexpected token in <interface>. (1)");
350 case STATE_INTERFACE_NAME
:
352 if (t
== XML_ATTRIBUTE_VALUE
) {
354 free(context
->interface_name
);
355 context
->interface_name
= name
;
359 state
= STATE_INTERFACE
;
361 log_error("Unexpected token in <interface>. (2)");
369 if (t
== XML_ATTRIBUTE_NAME
) {
370 if (streq_ptr(name
, "name"))
371 state
= STATE_METHOD_NAME
;
373 log_error("Unexpected <method> attribute %s", name
);
376 } else if (t
== XML_TAG_OPEN
) {
377 if (streq_ptr(name
, "arg"))
378 state
= STATE_METHOD_ARG
;
379 else if (streq_ptr(name
, "annotation")) {
380 r
= parse_xml_annotation(context
, &context
->member_flags
);
384 log_error("Unexpected <method> tag %s.", name
);
387 } else if (t
== XML_TAG_CLOSE_EMPTY
||
388 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "method"))) {
391 if (context
->ops
->on_method
) {
392 r
= context
->ops
->on_method(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_result
, context
->member_flags
, context
->userdata
);
397 context_reset_member(context
);
400 state
= STATE_INTERFACE
;
402 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
403 log_error("Unexpected token in <method> (1).");
409 case STATE_METHOD_NAME
:
411 if (t
== XML_ATTRIBUTE_VALUE
) {
414 free(context
->member_name
);
415 context
->member_name
= name
;
419 state
= STATE_METHOD
;
421 log_error("Unexpected token in <method> (2).");
427 case STATE_METHOD_ARG
:
429 if (t
== XML_ATTRIBUTE_NAME
) {
430 if (streq_ptr(name
, "name"))
431 state
= STATE_METHOD_ARG_NAME
;
432 else if (streq_ptr(name
, "type"))
433 state
= STATE_METHOD_ARG_TYPE
;
434 else if (streq_ptr(name
, "direction"))
435 state
= STATE_METHOD_ARG_DIRECTION
;
437 log_error("Unexpected method <arg> attribute %s.", name
);
440 } else if (t
== XML_TAG_OPEN
) {
441 if (streq_ptr(name
, "annotation")) {
442 r
= parse_xml_annotation(context
, NULL
);
446 log_error("Unexpected method <arg> tag %s.", name
);
449 } else if (t
== XML_TAG_CLOSE_EMPTY
||
450 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "arg"))) {
455 if (!argument_direction
|| streq(argument_direction
, "in")) {
456 if (!strextend(&context
->member_signature
, argument_type
, NULL
))
458 } else if (streq(argument_direction
, "out")) {
459 if (!strextend(&context
->member_result
, argument_type
, NULL
))
464 argument_type
= mfree(argument_type
);
465 argument_direction
= mfree(argument_direction
);
468 state
= STATE_METHOD
;
469 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
470 log_error("Unexpected token in method <arg>. (1)");
476 case STATE_METHOD_ARG_NAME
:
478 if (t
== XML_ATTRIBUTE_VALUE
)
479 state
= STATE_METHOD_ARG
;
481 log_error("Unexpected token in method <arg>. (2)");
487 case STATE_METHOD_ARG_TYPE
:
489 if (t
== XML_ATTRIBUTE_VALUE
) {
491 argument_type
= name
;
494 state
= STATE_METHOD_ARG
;
496 log_error("Unexpected token in method <arg>. (3)");
502 case STATE_METHOD_ARG_DIRECTION
:
504 if (t
== XML_ATTRIBUTE_VALUE
) {
505 free(argument_direction
);
506 argument_direction
= name
;
509 state
= STATE_METHOD_ARG
;
511 log_error("Unexpected token in method <arg>. (4)");
519 if (t
== XML_ATTRIBUTE_NAME
) {
520 if (streq_ptr(name
, "name"))
521 state
= STATE_SIGNAL_NAME
;
523 log_error("Unexpected <signal> attribute %s.", name
);
526 } else if (t
== XML_TAG_OPEN
) {
527 if (streq_ptr(name
, "arg"))
528 state
= STATE_SIGNAL_ARG
;
529 else if (streq_ptr(name
, "annotation")) {
530 r
= parse_xml_annotation(context
, &context
->member_flags
);
534 log_error("Unexpected <signal> tag %s.", name
);
537 } else if (t
== XML_TAG_CLOSE_EMPTY
||
538 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "signal"))) {
541 if (context
->ops
->on_signal
) {
542 r
= context
->ops
->on_signal(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_flags
, context
->userdata
);
547 context_reset_member(context
);
550 state
= STATE_INTERFACE
;
552 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
553 log_error("Unexpected token in <signal>. (1)");
559 case STATE_SIGNAL_NAME
:
561 if (t
== XML_ATTRIBUTE_VALUE
) {
564 free(context
->member_name
);
565 context
->member_name
= name
;
569 state
= STATE_SIGNAL
;
571 log_error("Unexpected token in <signal>. (2)");
578 case STATE_SIGNAL_ARG
:
580 if (t
== XML_ATTRIBUTE_NAME
) {
581 if (streq_ptr(name
, "name"))
582 state
= STATE_SIGNAL_ARG_NAME
;
583 else if (streq_ptr(name
, "type"))
584 state
= STATE_SIGNAL_ARG_TYPE
;
586 log_error("Unexpected signal <arg> attribute %s.", name
);
589 } else if (t
== XML_TAG_OPEN
) {
590 if (streq_ptr(name
, "annotation")) {
591 r
= parse_xml_annotation(context
, NULL
);
595 log_error("Unexpected signal <arg> tag %s.", name
);
598 } else if (t
== XML_TAG_CLOSE_EMPTY
||
599 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "arg"))) {
602 if (!strextend(&context
->member_signature
, argument_type
, NULL
))
605 argument_type
= mfree(argument_type
);
608 state
= STATE_SIGNAL
;
609 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
610 log_error("Unexpected token in signal <arg> (1).");
616 case STATE_SIGNAL_ARG_NAME
:
618 if (t
== XML_ATTRIBUTE_VALUE
)
619 state
= STATE_SIGNAL_ARG
;
621 log_error("Unexpected token in signal <arg> (2).");
627 case STATE_SIGNAL_ARG_TYPE
:
629 if (t
== XML_ATTRIBUTE_VALUE
) {
631 argument_type
= name
;
634 state
= STATE_SIGNAL_ARG
;
636 log_error("Unexpected token in signal <arg> (3).");
644 if (t
== XML_ATTRIBUTE_NAME
) {
645 if (streq_ptr(name
, "name"))
646 state
= STATE_PROPERTY_NAME
;
647 else if (streq_ptr(name
, "type"))
648 state
= STATE_PROPERTY_TYPE
;
649 else if (streq_ptr(name
, "access"))
650 state
= STATE_PROPERTY_ACCESS
;
652 log_error("Unexpected <property> attribute %s.", name
);
655 } else if (t
== XML_TAG_OPEN
) {
657 if (streq_ptr(name
, "annotation")) {
658 r
= parse_xml_annotation(context
, &context
->member_flags
);
662 log_error("Unexpected <property> tag %s.", name
);
666 } else if (t
== XML_TAG_CLOSE_EMPTY
||
667 (t
== XML_TAG_CLOSE
&& streq_ptr(name
, "property"))) {
670 if (context
->ops
->on_property
) {
671 r
= context
->ops
->on_property(context
->interface_name
, context
->member_name
, context
->member_signature
, context
->member_writable
, context
->member_flags
, context
->userdata
);
676 context_reset_member(context
);
679 state
= STATE_INTERFACE
;
681 } else if (t
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
682 log_error("Unexpected token in <property>. (1)");
688 case STATE_PROPERTY_NAME
:
690 if (t
== XML_ATTRIBUTE_VALUE
) {
693 free(context
->member_name
);
694 context
->member_name
= name
;
697 state
= STATE_PROPERTY
;
699 log_error("Unexpected token in <property>. (2)");
705 case STATE_PROPERTY_TYPE
:
707 if (t
== XML_ATTRIBUTE_VALUE
) {
710 free(context
->member_signature
);
711 context
->member_signature
= name
;
715 state
= STATE_PROPERTY
;
717 log_error("Unexpected token in <property>. (3)");
723 case STATE_PROPERTY_ACCESS
:
725 if (t
== XML_ATTRIBUTE_VALUE
) {
727 if (streq(name
, "readwrite") || streq(name
, "write"))
728 context
->member_writable
= true;
730 state
= STATE_PROPERTY
;
732 log_error("Unexpected token in <property>. (4)");
741 int parse_xml_introspect(const char *prefix
, const char *xml
, const XMLIntrospectOps
*ops
, void *userdata
) {
744 .userdata
= userdata
,
755 _cleanup_free_
char *name
= NULL
;
757 r
= xml_tokenize(&context
.current
, &name
, &context
.xml_state
, NULL
);
759 log_error("XML parse error");
768 if (r
== XML_TAG_OPEN
) {
770 if (streq(name
, "node")) {
771 r
= parse_xml_node(&context
, prefix
, 0);
775 log_error("Unexpected tag '%s' in introspection data.", name
);
779 } else if (r
!= XML_TEXT
|| !in_charset(name
, WHITESPACE
)) {
780 log_error("Unexpected token.");
787 context_reset_interface(&context
);