]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl-introspect.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / libsystemd / sd-bus / busctl-introspect.c
CommitLineData
a1ad3767
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
07630cea
LP
22#include "sd-bus.h"
23
24#include "string-util.h"
a1ad3767
LP
25#include "util.h"
26#include "xml.h"
a1ad3767
LP
27#include "busctl-introspect.h"
28
29#define NODE_DEPTH_MAX 16
30
31typedef struct Context {
32 const XMLIntrospectOps *ops;
33 void *userdata;
34
35 char *interface_name;
36 uint64_t interface_flags;
37
38 char *member_name;
39 char *member_signature;
40 char *member_result;
41 uint64_t member_flags;
0171da06 42 bool member_writable;
a1ad3767
LP
43
44 const char *current;
45 void *xml_state;
46} Context;
47
0171da06
LP
48static void context_reset_member(Context *c) {
49 free(c->member_name);
50 free(c->member_signature);
51 free(c->member_result);
52
53 c->member_name = c->member_signature = c->member_result = NULL;
54 c->member_flags = 0;
55 c->member_writable = false;
56}
57
58static void context_reset_interface(Context *c) {
a1e58e8e 59 c->interface_name = mfree(c->interface_name);
0171da06
LP
60 c->interface_flags = 0;
61
62 context_reset_member(c);
63}
64
a1ad3767
LP
65static int parse_xml_annotation(Context *context, uint64_t *flags) {
66
67 enum {
68 STATE_ANNOTATION,
69 STATE_NAME,
70 STATE_VALUE
71 } state = STATE_ANNOTATION;
72
73 _cleanup_free_ char *field = NULL, *value = NULL;
74
75 assert(context);
76
77 for (;;) {
78 _cleanup_free_ char *name = NULL;
79
80 int t;
81
82 t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
83 if (t < 0) {
84 log_error("XML parse error.");
85 return t;
86 }
87
88 if (t == XML_END) {
89 log_error("Premature end of XML data.");
90 return -EBADMSG;
91 }
92
93 switch (state) {
94
95 case STATE_ANNOTATION:
96
97 if (t == XML_ATTRIBUTE_NAME) {
98
99 if (streq_ptr(name, "name"))
100 state = STATE_NAME;
101
102 else if (streq_ptr(name, "value"))
103 state = STATE_VALUE;
104
105 else {
106 log_error("Unexpected <annotation> attribute %s.", name);
107 return -EBADMSG;
108 }
109
110 } else if (t == XML_TAG_CLOSE_EMPTY ||
111 (t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) {
112
113 if (flags) {
114 if (streq_ptr(field, "org.freedesktop.DBus.Deprecated")) {
115
116 if (streq_ptr(value, "true"))
117 *flags |= SD_BUS_VTABLE_DEPRECATED;
118
119 } else if (streq_ptr(field, "org.freedesktop.DBus.Method.NoReply")) {
120
121 if (streq_ptr(value, "true"))
122 *flags |= SD_BUS_VTABLE_METHOD_NO_REPLY;
123
124 } else if (streq_ptr(field, "org.freedesktop.DBus.Property.EmitsChangedSignal")) {
125
126 if (streq_ptr(value, "const"))
0171da06 127 *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) | SD_BUS_VTABLE_PROPERTY_CONST;
a1ad3767 128 else if (streq_ptr(value, "invalidates"))
0171da06 129 *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
a1ad3767 130 else if (streq_ptr(value, "false"))
0171da06 131 *flags = *flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION);
a1ad3767
LP
132 }
133 }
134
135 return 0;
136
137 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
138 log_error("Unexpected token in <annotation>. (1)");
139 return -EINVAL;
140 }
141
142 break;
143
144 case STATE_NAME:
145
146 if (t == XML_ATTRIBUTE_VALUE) {
147 free(field);
148 field = name;
149 name = NULL;
150
151 state = STATE_ANNOTATION;
152 } else {
153 log_error("Unexpected token in <annotation>. (2)");
154 return -EINVAL;
155 }
156
157 break;
158
159 case STATE_VALUE:
160
161 if (t == XML_ATTRIBUTE_VALUE) {
162 free(value);
163 value = name;
164 name = NULL;
165
166 state = STATE_ANNOTATION;
167 } else {
168 log_error("Unexpected token in <annotation>. (3)");
169 return -EINVAL;
170 }
171
172 break;
173
174 default:
175 assert_not_reached("Bad state");
176 }
177 }
178}
179
180static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) {
181
182 enum {
183 STATE_NODE,
184 STATE_NODE_NAME,
185 STATE_INTERFACE,
186 STATE_INTERFACE_NAME,
187 STATE_METHOD,
188 STATE_METHOD_NAME,
189 STATE_METHOD_ARG,
190 STATE_METHOD_ARG_NAME,
191 STATE_METHOD_ARG_TYPE,
192 STATE_METHOD_ARG_DIRECTION,
193 STATE_SIGNAL,
194 STATE_SIGNAL_NAME,
195 STATE_SIGNAL_ARG,
196 STATE_SIGNAL_ARG_NAME,
197 STATE_SIGNAL_ARG_TYPE,
198 STATE_PROPERTY,
199 STATE_PROPERTY_NAME,
200 STATE_PROPERTY_TYPE,
201 STATE_PROPERTY_ACCESS,
202 } state = STATE_NODE;
203
0171da06 204 _cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL;
a1ad3767
LP
205 const char *np = prefix;
206 int r;
207
208 assert(context);
209 assert(prefix);
210
211 if (n_depth > NODE_DEPTH_MAX) {
212 log_error("<node> depth too high.");
213 return -EINVAL;
214 }
215
216 for (;;) {
217 _cleanup_free_ char *name = NULL;
218 int t;
219
220 t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
221 if (t < 0) {
222 log_error("XML parse error.");
223 return t;
224 }
225
226 if (t == XML_END) {
227 log_error("Premature end of XML data.");
228 return -EBADMSG;
229 }
230
231 switch (state) {
232
233 case STATE_NODE:
234 if (t == XML_ATTRIBUTE_NAME) {
235
236 if (streq_ptr(name, "name"))
237 state = STATE_NODE_NAME;
238 else {
239 log_error("Unexpected <node> attribute %s.", name);
240 return -EBADMSG;
241 }
242
243 } else if (t == XML_TAG_OPEN) {
244
245 if (streq_ptr(name, "interface"))
246 state = STATE_INTERFACE;
a1ad3767
LP
247 else if (streq_ptr(name, "node")) {
248
249 r = parse_xml_node(context, np, n_depth+1);
250 if (r < 0)
251 return r;
252 } else {
253 log_error("Unexpected <node> tag %s.", name);
254 return -EBADMSG;
255 }
256
257 } else if (t == XML_TAG_CLOSE_EMPTY ||
258 (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) {
259
260 if (context->ops->on_path) {
261 r = context->ops->on_path(node_path ? node_path : np, context->userdata);
262 if (r < 0)
263 return r;
264 }
265
266 return 0;
267
268 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
269 log_error("Unexpected token in <node>. (1)");
270 return -EINVAL;
271 }
272
273 break;
274
275 case STATE_NODE_NAME:
276
277 if (t == XML_ATTRIBUTE_VALUE) {
278
279 free(node_path);
280
281 if (name[0] == '/') {
282 node_path = name;
283 name = NULL;
284 } else {
285
286 if (endswith(prefix, "/"))
287 node_path = strappend(prefix, name);
288 else
289 node_path = strjoin(prefix, "/", name, NULL);
290 if (!node_path)
291 return log_oom();
292 }
293
294 np = node_path;
295 state = STATE_NODE;
296 } else {
297 log_error("Unexpected token in <node>. (2)");
298 return -EINVAL;
299 }
300
301 break;
302
303 case STATE_INTERFACE:
304
305 if (t == XML_ATTRIBUTE_NAME) {
306 if (streq_ptr(name, "name"))
307 state = STATE_INTERFACE_NAME;
308 else {
309 log_error("Unexpected <interface> attribute %s.", name);
310 return -EBADMSG;
311 }
312
313 } else if (t == XML_TAG_OPEN) {
314 if (streq_ptr(name, "method"))
315 state = STATE_METHOD;
316 else if (streq_ptr(name, "signal"))
317 state = STATE_SIGNAL;
0171da06
LP
318 else if (streq_ptr(name, "property")) {
319 context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
a1ad3767 320 state = STATE_PROPERTY;
0171da06 321 } else if (streq_ptr(name, "annotation")) {
a1ad3767
LP
322 r = parse_xml_annotation(context, &context->interface_flags);
323 if (r < 0)
324 return r;
325 } else {
326 log_error("Unexpected <interface> tag %s.", name);
327 return -EINVAL;
328 }
329 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
330 (t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) {
331
332 if (n_depth == 0) {
333 if (context->ops->on_interface) {
334 r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata);
335 if (r < 0)
336 return r;
337 }
338
339 context_reset_interface(context);
340 }
a1ad3767
LP
341
342 state = STATE_NODE;
343
0171da06 344 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
345 log_error("Unexpected token in <interface>. (1)");
346 return -EINVAL;
347 }
348
349 break;
350
351 case STATE_INTERFACE_NAME:
352
0171da06
LP
353 if (t == XML_ATTRIBUTE_VALUE) {
354 if (n_depth == 0) {
355 free(context->interface_name);
356 context->interface_name = name;
357 name = NULL;
358 }
359
a1ad3767 360 state = STATE_INTERFACE;
0171da06 361 } else {
a1ad3767
LP
362 log_error("Unexpected token in <interface>. (2)");
363 return -EINVAL;
364 }
365
366 break;
367
368 case STATE_METHOD:
369
370 if (t == XML_ATTRIBUTE_NAME) {
371 if (streq_ptr(name, "name"))
372 state = STATE_METHOD_NAME;
373 else {
374 log_error("Unexpected <method> attribute %s", name);
375 return -EBADMSG;
376 }
377 } else if (t == XML_TAG_OPEN) {
378 if (streq_ptr(name, "arg"))
379 state = STATE_METHOD_ARG;
380 else if (streq_ptr(name, "annotation")) {
381 r = parse_xml_annotation(context, &context->member_flags);
382 if (r < 0)
383 return r;
384 } else {
385 log_error("Unexpected <method> tag %s.", name);
386 return -EINVAL;
387 }
388 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
389 (t == XML_TAG_CLOSE && streq_ptr(name, "method"))) {
390
391 if (n_depth == 0) {
392 if (context->ops->on_method) {
393 r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata);
394 if (r < 0)
395 return r;
396 }
397
398 context_reset_member(context);
399 }
a1ad3767
LP
400
401 state = STATE_INTERFACE;
402
0171da06 403 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
404 log_error("Unexpected token in <method> (1).");
405 return -EINVAL;
406 }
407
408 break;
409
410 case STATE_METHOD_NAME:
411
0171da06
LP
412 if (t == XML_ATTRIBUTE_VALUE) {
413
414 if (n_depth == 0) {
415 free(context->member_name);
416 context->member_name = name;
417 name = NULL;
418 }
419
a1ad3767 420 state = STATE_METHOD;
0171da06 421 } else {
a1ad3767
LP
422 log_error("Unexpected token in <method> (2).");
423 return -EINVAL;
424 }
425
426 break;
427
428 case STATE_METHOD_ARG:
429
430 if (t == XML_ATTRIBUTE_NAME) {
431 if (streq_ptr(name, "name"))
432 state = STATE_METHOD_ARG_NAME;
433 else if (streq_ptr(name, "type"))
434 state = STATE_METHOD_ARG_TYPE;
435 else if (streq_ptr(name, "direction"))
436 state = STATE_METHOD_ARG_DIRECTION;
437 else {
438 log_error("Unexpected method <arg> attribute %s.", name);
439 return -EBADMSG;
440 }
441 } else if (t == XML_TAG_OPEN) {
442 if (streq_ptr(name, "annotation")) {
443 r = parse_xml_annotation(context, NULL);
444 if (r < 0)
445 return r;
446 } else {
447 log_error("Unexpected method <arg> tag %s.", name);
448 return -EINVAL;
449 }
450 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
451 (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
452
453 if (n_depth == 0) {
454
455 if (argument_type) {
456 if (!argument_direction || streq(argument_direction, "in")) {
457 if (!strextend(&context->member_signature, argument_type, NULL))
458 return log_oom();
459 } else if (streq(argument_direction, "out")) {
460 if (!strextend(&context->member_result, argument_type, NULL))
461 return log_oom();
462 }
463 }
a1ad3767 464
97b11eed
DH
465 argument_type = mfree(argument_type);
466 argument_direction = mfree(argument_direction);
0171da06 467 }
a1ad3767 468
0171da06
LP
469 state = STATE_METHOD;
470 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
471 log_error("Unexpected token in method <arg>. (1)");
472 return -EINVAL;
473 }
474
475 break;
476
477 case STATE_METHOD_ARG_NAME:
478
479 if (t == XML_ATTRIBUTE_VALUE)
480 state = STATE_METHOD_ARG;
481 else {
482 log_error("Unexpected token in method <arg>. (2)");
483 return -EINVAL;
484 }
485
486 break;
487
488 case STATE_METHOD_ARG_TYPE:
489
0171da06
LP
490 if (t == XML_ATTRIBUTE_VALUE) {
491 free(argument_type);
492 argument_type = name;
493 name = NULL;
494
a1ad3767 495 state = STATE_METHOD_ARG;
0171da06 496 } else {
a1ad3767
LP
497 log_error("Unexpected token in method <arg>. (3)");
498 return -EINVAL;
499 }
500
501 break;
502
503 case STATE_METHOD_ARG_DIRECTION:
504
0171da06
LP
505 if (t == XML_ATTRIBUTE_VALUE) {
506 free(argument_direction);
507 argument_direction = name;
508 name = NULL;
509
a1ad3767 510 state = STATE_METHOD_ARG;
0171da06 511 } else {
a1ad3767
LP
512 log_error("Unexpected token in method <arg>. (4)");
513 return -EINVAL;
514 }
515
516 break;
517
518 case STATE_SIGNAL:
519
520 if (t == XML_ATTRIBUTE_NAME) {
521 if (streq_ptr(name, "name"))
522 state = STATE_SIGNAL_NAME;
523 else {
524 log_error("Unexpected <signal> attribute %s.", name);
525 return -EBADMSG;
526 }
527 } else if (t == XML_TAG_OPEN) {
528 if (streq_ptr(name, "arg"))
529 state = STATE_SIGNAL_ARG;
530 else if (streq_ptr(name, "annotation")) {
531 r = parse_xml_annotation(context, &context->member_flags);
532 if (r < 0)
533 return r;
534 } else {
535 log_error("Unexpected <signal> tag %s.", name);
536 return -EINVAL;
537 }
538 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
539 (t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) {
540
541 if (n_depth == 0) {
542 if (context->ops->on_signal) {
543 r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata);
544 if (r < 0)
545 return r;
546 }
547
548 context_reset_member(context);
549 }
a1ad3767
LP
550
551 state = STATE_INTERFACE;
552
0171da06 553 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
554 log_error("Unexpected token in <signal>. (1)");
555 return -EINVAL;
556 }
557
558 break;
559
560 case STATE_SIGNAL_NAME:
561
0171da06
LP
562 if (t == XML_ATTRIBUTE_VALUE) {
563
564 if (n_depth == 0) {
565 free(context->member_name);
566 context->member_name = name;
567 name = NULL;
568 }
569
a1ad3767 570 state = STATE_SIGNAL;
0171da06 571 } else {
a1ad3767
LP
572 log_error("Unexpected token in <signal>. (2)");
573 return -EINVAL;
574 }
575
576 break;
577
578
579 case STATE_SIGNAL_ARG:
580
581 if (t == XML_ATTRIBUTE_NAME) {
582 if (streq_ptr(name, "name"))
583 state = STATE_SIGNAL_ARG_NAME;
584 else if (streq_ptr(name, "type"))
585 state = STATE_SIGNAL_ARG_TYPE;
586 else {
587 log_error("Unexpected signal <arg> attribute %s.", name);
588 return -EBADMSG;
589 }
590 } else if (t == XML_TAG_OPEN) {
591 if (streq_ptr(name, "annotation")) {
592 r = parse_xml_annotation(context, NULL);
593 if (r < 0)
594 return r;
595 } else {
596 log_error("Unexpected signal <arg> tag %s.", name);
597 return -EINVAL;
598 }
599 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06 600 (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
a1ad3767 601
0171da06
LP
602 if (argument_type) {
603 if (!strextend(&context->member_signature, argument_type, NULL))
604 return log_oom();
a1ad3767 605
97b11eed 606 argument_type = mfree(argument_type);
0171da06
LP
607 }
608
609 state = STATE_SIGNAL;
610 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
611 log_error("Unexpected token in signal <arg> (1).");
612 return -EINVAL;
613 }
614
615 break;
616
617 case STATE_SIGNAL_ARG_NAME:
618
619 if (t == XML_ATTRIBUTE_VALUE)
620 state = STATE_SIGNAL_ARG;
621 else {
622 log_error("Unexpected token in signal <arg> (2).");
623 return -EINVAL;
624 }
625
626 break;
627
628 case STATE_SIGNAL_ARG_TYPE:
629
0171da06
LP
630 if (t == XML_ATTRIBUTE_VALUE) {
631 free(argument_type);
632 argument_type = name;
633 name = NULL;
634
a1ad3767 635 state = STATE_SIGNAL_ARG;
0171da06 636 } else {
a1ad3767
LP
637 log_error("Unexpected token in signal <arg> (3).");
638 return -EINVAL;
639 }
640
641 break;
642
643 case STATE_PROPERTY:
644
645 if (t == XML_ATTRIBUTE_NAME) {
646 if (streq_ptr(name, "name"))
647 state = STATE_PROPERTY_NAME;
648 else if (streq_ptr(name, "type"))
649 state = STATE_PROPERTY_TYPE;
650 else if (streq_ptr(name, "access"))
651 state = STATE_PROPERTY_ACCESS;
652 else {
653 log_error("Unexpected <property> attribute %s.", name);
654 return -EBADMSG;
655 }
656 } else if (t == XML_TAG_OPEN) {
657
658 if (streq_ptr(name, "annotation")) {
659 r = parse_xml_annotation(context, &context->member_flags);
660 if (r < 0)
661 return r;
662 } else {
663 log_error("Unexpected <property> tag %s.", name);
664 return -EINVAL;
665 }
666
667 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
668 (t == XML_TAG_CLOSE && streq_ptr(name, "property"))) {
669
670 if (n_depth == 0) {
671 if (context->ops->on_property) {
672 r = context->ops->on_property(context->interface_name, context->member_name, context->member_signature, context->member_writable, context->member_flags, context->userdata);
673 if (r < 0)
674 return r;
675 }
676
677 context_reset_member(context);
678 }
a1ad3767
LP
679
680 state = STATE_INTERFACE;
681
0171da06 682 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
683 log_error("Unexpected token in <property>. (1)");
684 return -EINVAL;
685 }
686
687 break;
688
689 case STATE_PROPERTY_NAME:
690
0171da06
LP
691 if (t == XML_ATTRIBUTE_VALUE) {
692
693 if (n_depth == 0) {
694 free(context->member_name);
695 context->member_name = name;
696 name = NULL;
697 }
a1ad3767 698 state = STATE_PROPERTY;
0171da06 699 } else {
a1ad3767
LP
700 log_error("Unexpected token in <property>. (2)");
701 return -EINVAL;
702 }
703
704 break;
705
706 case STATE_PROPERTY_TYPE:
707
0171da06
LP
708 if (t == XML_ATTRIBUTE_VALUE) {
709
710 if (n_depth == 0) {
711 free(context->member_signature);
712 context->member_signature = name;
713 name = NULL;
714 }
715
a1ad3767 716 state = STATE_PROPERTY;
0171da06 717 } else {
a1ad3767
LP
718 log_error("Unexpected token in <property>. (3)");
719 return -EINVAL;
720 }
721
722 break;
723
724 case STATE_PROPERTY_ACCESS:
725
0171da06
LP
726 if (t == XML_ATTRIBUTE_VALUE) {
727
728 if (streq(name, "readwrite") || streq(name, "write"))
729 context->member_writable = true;
730
a1ad3767 731 state = STATE_PROPERTY;
0171da06 732 } else {
a1ad3767
LP
733 log_error("Unexpected token in <property>. (4)");
734 return -EINVAL;
735 }
736
737 break;
738 }
739 }
740}
741
742int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) {
743 Context context = {
744 .ops = ops,
745 .userdata = userdata,
746 .current = xml,
747 };
748
749 int r;
750
751 assert(prefix);
752 assert(xml);
753 assert(ops);
754
755 for (;;) {
756 _cleanup_free_ char *name = NULL;
757
758 r = xml_tokenize(&context.current, &name, &context.xml_state, NULL);
759 if (r < 0) {
760 log_error("XML parse error");
0171da06 761 goto finish;
a1ad3767
LP
762 }
763
0171da06
LP
764 if (r == XML_END) {
765 r = 0;
a1ad3767 766 break;
0171da06 767 }
a1ad3767
LP
768
769 if (r == XML_TAG_OPEN) {
770
771 if (streq(name, "node")) {
772 r = parse_xml_node(&context, prefix, 0);
773 if (r < 0)
0171da06 774 goto finish;
a1ad3767
LP
775 } else {
776 log_error("Unexpected tag '%s' in introspection data.", name);
0171da06
LP
777 r = -EBADMSG;
778 goto finish;
a1ad3767
LP
779 }
780 } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
781 log_error("Unexpected token.");
0171da06
LP
782 r = -EBADMSG;
783 goto finish;
a1ad3767
LP
784 }
785 }
786
0171da06
LP
787finish:
788 context_reset_interface(&context);
789
790 return r;
a1ad3767 791}