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