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;
46 static int parse_xml_annotation(Context *context, uint64_t *flags) {
52 } state = STATE_ANNOTATION;
54 _cleanup_free_ char *field = NULL, *value = NULL;
59 _cleanup_free_ char *name = NULL;
63 t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
65 log_error("XML parse error.");
70 log_error("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 log_error("Unexpected <annotation> attribute %s.", name);
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 |= SD_BUS_VTABLE_PROPERTY_CONST;
109 else if (streq_ptr(value, "invalidates"))
110 *flags |= SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
111 else if (streq_ptr(value, "false"))
112 *flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
118 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
119 log_error("Unexpected token in <annotation>. (1)");
127 if (t == XML_ATTRIBUTE_VALUE) {
132 state = STATE_ANNOTATION;
134 log_error("Unexpected token in <annotation>. (2)");
142 if (t == XML_ATTRIBUTE_VALUE) {
147 state = STATE_ANNOTATION;
149 log_error("Unexpected token in <annotation>. (3)");
156 assert_not_reached("Bad state");
161 static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) {
167 STATE_INTERFACE_NAME,
171 STATE_METHOD_ARG_NAME,
172 STATE_METHOD_ARG_TYPE,
173 STATE_METHOD_ARG_DIRECTION,
177 STATE_SIGNAL_ARG_NAME,
178 STATE_SIGNAL_ARG_TYPE,
182 STATE_PROPERTY_ACCESS,
183 } state = STATE_NODE;
185 _cleanup_free_ char *node_path = NULL;
186 const char *np = prefix;
192 if (n_depth > NODE_DEPTH_MAX) {
193 log_error("<node> depth too high.");
198 _cleanup_free_ char *name = NULL;
201 t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
203 log_error("XML parse error.");
208 log_error("Premature end of XML data.");
215 if (t == XML_ATTRIBUTE_NAME) {
217 if (streq_ptr(name, "name"))
218 state = STATE_NODE_NAME;
220 log_error("Unexpected <node> attribute %s.", name);
224 } else if (t == XML_TAG_OPEN) {
226 if (streq_ptr(name, "interface"))
227 state = STATE_INTERFACE;
229 else if (streq_ptr(name, "node")) {
231 r = parse_xml_node(context, np, n_depth+1);
235 log_error("Unexpected <node> tag %s.", name);
239 } else if (t == XML_TAG_CLOSE_EMPTY ||
240 (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) {
242 if (context->ops->on_path) {
243 r = context->ops->on_path(node_path ? node_path : np, context->userdata);
250 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
251 log_error("Unexpected token in <node>. (1)");
257 case STATE_NODE_NAME:
259 if (t == XML_ATTRIBUTE_VALUE) {
263 if (name[0] == '/') {
268 if (endswith(prefix, "/"))
269 node_path = strappend(prefix, name);
271 node_path = strjoin(prefix, "/", name, NULL);
279 log_error("Unexpected token in <node>. (2)");
285 case STATE_INTERFACE:
287 if (t == XML_ATTRIBUTE_NAME) {
288 if (streq_ptr(name, "name"))
289 state = STATE_INTERFACE_NAME;
291 log_error("Unexpected <interface> attribute %s.", name);
295 } else if (t == XML_TAG_OPEN) {
296 if (streq_ptr(name, "method"))
297 state = STATE_METHOD;
298 else if (streq_ptr(name, "signal"))
299 state = STATE_SIGNAL;
300 else if (streq_ptr(name, "property"))
301 state = STATE_PROPERTY;
302 else if (streq_ptr(name, "annotation")) {
303 r = parse_xml_annotation(context, &context->interface_flags);
307 log_error("Unexpected <interface> tag %s.", name);
310 } else if (t == XML_TAG_CLOSE_EMPTY ||
311 (t == XML_TAG_CLOSE && streq_ptr(name, "interface")))
315 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
316 log_error("Unexpected token in <interface>. (1)");
322 case STATE_INTERFACE_NAME:
324 if (t == XML_ATTRIBUTE_VALUE)
325 state = STATE_INTERFACE;
327 log_error("Unexpected token in <interface>. (2)");
335 if (t == XML_ATTRIBUTE_NAME) {
336 if (streq_ptr(name, "name"))
337 state = STATE_METHOD_NAME;
339 log_error("Unexpected <method> attribute %s", name);
342 } else if (t == XML_TAG_OPEN) {
343 if (streq_ptr(name, "arg"))
344 state = STATE_METHOD_ARG;
345 else if (streq_ptr(name, "annotation")) {
346 r = parse_xml_annotation(context, &context->member_flags);
350 log_error("Unexpected <method> tag %s.", name);
353 } else if (t == XML_TAG_CLOSE_EMPTY ||
354 (t == XML_TAG_CLOSE && streq_ptr(name, "method")))
356 state = STATE_INTERFACE;
358 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
359 log_error("Unexpected token in <method> (1).");
365 case STATE_METHOD_NAME:
367 if (t == XML_ATTRIBUTE_VALUE)
368 state = STATE_METHOD;
370 log_error("Unexpected token in <method> (2).");
376 case STATE_METHOD_ARG:
378 if (t == XML_ATTRIBUTE_NAME) {
379 if (streq_ptr(name, "name"))
380 state = STATE_METHOD_ARG_NAME;
381 else if (streq_ptr(name, "type"))
382 state = STATE_METHOD_ARG_TYPE;
383 else if (streq_ptr(name, "direction"))
384 state = STATE_METHOD_ARG_DIRECTION;
386 log_error("Unexpected method <arg> attribute %s.", name);
389 } else if (t == XML_TAG_OPEN) {
390 if (streq_ptr(name, "annotation")) {
391 r = parse_xml_annotation(context, NULL);
395 log_error("Unexpected method <arg> tag %s.", name);
398 } else if (t == XML_TAG_CLOSE_EMPTY ||
399 (t == XML_TAG_CLOSE && streq_ptr(name, "arg")))
401 state = STATE_METHOD;
403 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
404 log_error("Unexpected token in method <arg>. (1)");
410 case STATE_METHOD_ARG_NAME:
412 if (t == XML_ATTRIBUTE_VALUE)
413 state = STATE_METHOD_ARG;
415 log_error("Unexpected token in method <arg>. (2)");
421 case STATE_METHOD_ARG_TYPE:
423 if (t == XML_ATTRIBUTE_VALUE)
424 state = STATE_METHOD_ARG;
426 log_error("Unexpected token in method <arg>. (3)");
432 case STATE_METHOD_ARG_DIRECTION:
434 if (t == XML_ATTRIBUTE_VALUE)
435 state = STATE_METHOD_ARG;
437 log_error("Unexpected token in method <arg>. (4)");
445 if (t == XML_ATTRIBUTE_NAME) {
446 if (streq_ptr(name, "name"))
447 state = STATE_SIGNAL_NAME;
449 log_error("Unexpected <signal> attribute %s.", name);
452 } else if (t == XML_TAG_OPEN) {
453 if (streq_ptr(name, "arg"))
454 state = STATE_SIGNAL_ARG;
455 else if (streq_ptr(name, "annotation")) {
456 r = parse_xml_annotation(context, &context->member_flags);
460 log_error("Unexpected <signal> tag %s.", name);
463 } else if (t == XML_TAG_CLOSE_EMPTY ||
464 (t == XML_TAG_CLOSE && streq_ptr(name, "signal")))
466 state = STATE_INTERFACE;
468 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
469 log_error("Unexpected token in <signal>. (1)");
475 case STATE_SIGNAL_NAME:
477 if (t == XML_ATTRIBUTE_VALUE)
478 state = STATE_SIGNAL;
480 log_error("Unexpected token in <signal>. (2)");
487 case STATE_SIGNAL_ARG:
489 if (t == XML_ATTRIBUTE_NAME) {
490 if (streq_ptr(name, "name"))
491 state = STATE_SIGNAL_ARG_NAME;
492 else if (streq_ptr(name, "type"))
493 state = STATE_SIGNAL_ARG_TYPE;
495 log_error("Unexpected signal <arg> attribute %s.", name);
498 } else if (t == XML_TAG_OPEN) {
499 if (streq_ptr(name, "annotation")) {
500 r = parse_xml_annotation(context, NULL);
504 log_error("Unexpected signal <arg> tag %s.", name);
507 } else if (t == XML_TAG_CLOSE_EMPTY ||
508 (t == XML_TAG_CLOSE && streq_ptr(name, "arg")))
510 state = STATE_SIGNAL;
512 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
513 log_error("Unexpected token in signal <arg> (1).");
519 case STATE_SIGNAL_ARG_NAME:
521 if (t == XML_ATTRIBUTE_VALUE)
522 state = STATE_SIGNAL_ARG;
524 log_error("Unexpected token in signal <arg> (2).");
530 case STATE_SIGNAL_ARG_TYPE:
532 if (t == XML_ATTRIBUTE_VALUE)
533 state = STATE_SIGNAL_ARG;
535 log_error("Unexpected token in signal <arg> (3).");
543 if (t == XML_ATTRIBUTE_NAME) {
544 if (streq_ptr(name, "name"))
545 state = STATE_PROPERTY_NAME;
546 else if (streq_ptr(name, "type"))
547 state = STATE_PROPERTY_TYPE;
548 else if (streq_ptr(name, "access"))
549 state = STATE_PROPERTY_ACCESS;
551 log_error("Unexpected <property> attribute %s.", name);
554 } else if (t == XML_TAG_OPEN) {
556 if (streq_ptr(name, "annotation")) {
557 r = parse_xml_annotation(context, &context->member_flags);
561 log_error("Unexpected <property> tag %s.", name);
565 } else if (t == XML_TAG_CLOSE_EMPTY ||
566 (t == XML_TAG_CLOSE && streq_ptr(name, "property")))
568 state = STATE_INTERFACE;
570 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
571 log_error("Unexpected token in <property>. (1)");
577 case STATE_PROPERTY_NAME:
579 if (t == XML_ATTRIBUTE_VALUE)
580 state = STATE_PROPERTY;
582 log_error("Unexpected token in <property>. (2)");
588 case STATE_PROPERTY_TYPE:
590 if (t == XML_ATTRIBUTE_VALUE)
591 state = STATE_PROPERTY;
593 log_error("Unexpected token in <property>. (3)");
599 case STATE_PROPERTY_ACCESS:
601 if (t == XML_ATTRIBUTE_VALUE)
602 state = STATE_PROPERTY;
604 log_error("Unexpected token in <property>. (4)");
613 int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) {
616 .userdata = userdata,
627 _cleanup_free_ char *name = NULL;
629 r = xml_tokenize(&context.current, &name, &context.xml_state, NULL);
631 log_error("XML parse error");
638 if (r == XML_TAG_OPEN) {
640 if (streq(name, "node")) {
641 r = parse_xml_node(&context, prefix, 0);
645 log_error("Unexpected tag '%s' in introspection data.", name);
648 } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
649 log_error("Unexpected token.");