]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/busctl/busctl-introspect.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / busctl / busctl-introspect.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a1ad3767
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
07630cea
LP
21#include "sd-bus.h"
22
b5efdb8a
LP
23#include "alloc-util.h"
24#include "busctl-introspect.h"
07630cea 25#include "string-util.h"
a1ad3767
LP
26#include "util.h"
27#include "xml.h"
a1ad3767
LP
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) {
057a32ef 147 free_and_replace(field, name);
a1ad3767
LP
148
149 state = STATE_ANNOTATION;
150 } else {
151 log_error("Unexpected token in <annotation>. (2)");
152 return -EINVAL;
153 }
154
155 break;
156
157 case STATE_VALUE:
158
159 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef 160 free_and_replace(value, name);
a1ad3767
LP
161
162 state = STATE_ANNOTATION;
163 } else {
164 log_error("Unexpected token in <annotation>. (3)");
165 return -EINVAL;
166 }
167
168 break;
169
170 default:
171 assert_not_reached("Bad state");
172 }
173 }
174}
175
176static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) {
177
178 enum {
179 STATE_NODE,
180 STATE_NODE_NAME,
181 STATE_INTERFACE,
182 STATE_INTERFACE_NAME,
183 STATE_METHOD,
184 STATE_METHOD_NAME,
185 STATE_METHOD_ARG,
186 STATE_METHOD_ARG_NAME,
187 STATE_METHOD_ARG_TYPE,
188 STATE_METHOD_ARG_DIRECTION,
189 STATE_SIGNAL,
190 STATE_SIGNAL_NAME,
191 STATE_SIGNAL_ARG,
192 STATE_SIGNAL_ARG_NAME,
193 STATE_SIGNAL_ARG_TYPE,
874899e1 194 STATE_SIGNAL_ARG_DIRECTION,
a1ad3767
LP
195 STATE_PROPERTY,
196 STATE_PROPERTY_NAME,
197 STATE_PROPERTY_TYPE,
198 STATE_PROPERTY_ACCESS,
199 } state = STATE_NODE;
200
0171da06 201 _cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL;
a1ad3767
LP
202 const char *np = prefix;
203 int r;
204
205 assert(context);
206 assert(prefix);
207
208 if (n_depth > NODE_DEPTH_MAX) {
209 log_error("<node> depth too high.");
210 return -EINVAL;
211 }
212
213 for (;;) {
214 _cleanup_free_ char *name = NULL;
215 int t;
216
217 t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
218 if (t < 0) {
219 log_error("XML parse error.");
220 return t;
221 }
222
223 if (t == XML_END) {
224 log_error("Premature end of XML data.");
225 return -EBADMSG;
226 }
227
228 switch (state) {
229
230 case STATE_NODE:
231 if (t == XML_ATTRIBUTE_NAME) {
232
233 if (streq_ptr(name, "name"))
234 state = STATE_NODE_NAME;
235 else {
236 log_error("Unexpected <node> attribute %s.", name);
237 return -EBADMSG;
238 }
239
240 } else if (t == XML_TAG_OPEN) {
241
242 if (streq_ptr(name, "interface"))
243 state = STATE_INTERFACE;
a1ad3767
LP
244 else if (streq_ptr(name, "node")) {
245
246 r = parse_xml_node(context, np, n_depth+1);
247 if (r < 0)
248 return r;
249 } else {
250 log_error("Unexpected <node> tag %s.", name);
251 return -EBADMSG;
252 }
253
254 } else if (t == XML_TAG_CLOSE_EMPTY ||
255 (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) {
256
257 if (context->ops->on_path) {
258 r = context->ops->on_path(node_path ? node_path : np, context->userdata);
259 if (r < 0)
260 return r;
261 }
262
263 return 0;
264
265 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
266 log_error("Unexpected token in <node>. (1)");
267 return -EINVAL;
268 }
269
270 break;
271
272 case STATE_NODE_NAME:
273
274 if (t == XML_ATTRIBUTE_VALUE) {
275
276 free(node_path);
277
278 if (name[0] == '/') {
279 node_path = name;
280 name = NULL;
281 } else {
282
283 if (endswith(prefix, "/"))
284 node_path = strappend(prefix, name);
285 else
605405c6 286 node_path = strjoin(prefix, "/", name);
a1ad3767
LP
287 if (!node_path)
288 return log_oom();
289 }
290
291 np = node_path;
292 state = STATE_NODE;
293 } else {
294 log_error("Unexpected token in <node>. (2)");
295 return -EINVAL;
296 }
297
298 break;
299
300 case STATE_INTERFACE:
301
302 if (t == XML_ATTRIBUTE_NAME) {
303 if (streq_ptr(name, "name"))
304 state = STATE_INTERFACE_NAME;
305 else {
306 log_error("Unexpected <interface> attribute %s.", name);
307 return -EBADMSG;
308 }
309
310 } else if (t == XML_TAG_OPEN) {
311 if (streq_ptr(name, "method"))
312 state = STATE_METHOD;
313 else if (streq_ptr(name, "signal"))
314 state = STATE_SIGNAL;
0171da06
LP
315 else if (streq_ptr(name, "property")) {
316 context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
a1ad3767 317 state = STATE_PROPERTY;
0171da06 318 } else if (streq_ptr(name, "annotation")) {
a1ad3767
LP
319 r = parse_xml_annotation(context, &context->interface_flags);
320 if (r < 0)
321 return r;
322 } else {
323 log_error("Unexpected <interface> tag %s.", name);
324 return -EINVAL;
325 }
326 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
327 (t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) {
328
329 if (n_depth == 0) {
330 if (context->ops->on_interface) {
331 r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata);
332 if (r < 0)
333 return r;
334 }
335
336 context_reset_interface(context);
337 }
a1ad3767
LP
338
339 state = STATE_NODE;
340
0171da06 341 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
342 log_error("Unexpected token in <interface>. (1)");
343 return -EINVAL;
344 }
345
346 break;
347
348 case STATE_INTERFACE_NAME:
349
0171da06 350 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef
MM
351 if (n_depth == 0)
352 free_and_replace(context->interface_name, name);
0171da06 353
a1ad3767 354 state = STATE_INTERFACE;
0171da06 355 } else {
a1ad3767
LP
356 log_error("Unexpected token in <interface>. (2)");
357 return -EINVAL;
358 }
359
360 break;
361
362 case STATE_METHOD:
363
364 if (t == XML_ATTRIBUTE_NAME) {
365 if (streq_ptr(name, "name"))
366 state = STATE_METHOD_NAME;
367 else {
368 log_error("Unexpected <method> attribute %s", name);
369 return -EBADMSG;
370 }
371 } else if (t == XML_TAG_OPEN) {
372 if (streq_ptr(name, "arg"))
373 state = STATE_METHOD_ARG;
374 else if (streq_ptr(name, "annotation")) {
375 r = parse_xml_annotation(context, &context->member_flags);
376 if (r < 0)
377 return r;
378 } else {
379 log_error("Unexpected <method> tag %s.", name);
380 return -EINVAL;
381 }
382 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
383 (t == XML_TAG_CLOSE && streq_ptr(name, "method"))) {
384
385 if (n_depth == 0) {
386 if (context->ops->on_method) {
387 r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata);
388 if (r < 0)
389 return r;
390 }
391
392 context_reset_member(context);
393 }
a1ad3767
LP
394
395 state = STATE_INTERFACE;
396
0171da06 397 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
398 log_error("Unexpected token in <method> (1).");
399 return -EINVAL;
400 }
401
402 break;
403
404 case STATE_METHOD_NAME:
405
0171da06 406 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef
MM
407 if (n_depth == 0)
408 free_and_replace(context->member_name, name);
0171da06 409
a1ad3767 410 state = STATE_METHOD;
0171da06 411 } else {
a1ad3767
LP
412 log_error("Unexpected token in <method> (2).");
413 return -EINVAL;
414 }
415
416 break;
417
418 case STATE_METHOD_ARG:
419
420 if (t == XML_ATTRIBUTE_NAME) {
421 if (streq_ptr(name, "name"))
422 state = STATE_METHOD_ARG_NAME;
423 else if (streq_ptr(name, "type"))
424 state = STATE_METHOD_ARG_TYPE;
425 else if (streq_ptr(name, "direction"))
874899e1 426 state = STATE_METHOD_ARG_DIRECTION;
a1ad3767
LP
427 else {
428 log_error("Unexpected method <arg> attribute %s.", name);
429 return -EBADMSG;
430 }
431 } else if (t == XML_TAG_OPEN) {
432 if (streq_ptr(name, "annotation")) {
433 r = parse_xml_annotation(context, NULL);
434 if (r < 0)
435 return r;
436 } else {
437 log_error("Unexpected method <arg> tag %s.", name);
438 return -EINVAL;
439 }
440 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
441 (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
442
443 if (n_depth == 0) {
444
445 if (argument_type) {
446 if (!argument_direction || streq(argument_direction, "in")) {
447 if (!strextend(&context->member_signature, argument_type, NULL))
448 return log_oom();
449 } else if (streq(argument_direction, "out")) {
450 if (!strextend(&context->member_result, argument_type, NULL))
451 return log_oom();
874899e1
MM
452 } else
453 log_error("Unexpected method <arg> direction value '%s'.", argument_direction);
0171da06 454 }
a1ad3767 455
97b11eed
DH
456 argument_type = mfree(argument_type);
457 argument_direction = mfree(argument_direction);
0171da06 458 }
a1ad3767 459
0171da06
LP
460 state = STATE_METHOD;
461 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
462 log_error("Unexpected token in method <arg>. (1)");
463 return -EINVAL;
464 }
465
466 break;
467
468 case STATE_METHOD_ARG_NAME:
469
470 if (t == XML_ATTRIBUTE_VALUE)
471 state = STATE_METHOD_ARG;
472 else {
473 log_error("Unexpected token in method <arg>. (2)");
474 return -EINVAL;
475 }
476
477 break;
478
479 case STATE_METHOD_ARG_TYPE:
480
0171da06 481 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef 482 free_and_replace(argument_type, name);
0171da06 483
a1ad3767 484 state = STATE_METHOD_ARG;
0171da06 485 } else {
a1ad3767
LP
486 log_error("Unexpected token in method <arg>. (3)");
487 return -EINVAL;
488 }
489
490 break;
491
492 case STATE_METHOD_ARG_DIRECTION:
493
0171da06 494 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef 495 free_and_replace(argument_direction, name);
0171da06 496
a1ad3767 497 state = STATE_METHOD_ARG;
0171da06 498 } else {
a1ad3767
LP
499 log_error("Unexpected token in method <arg>. (4)");
500 return -EINVAL;
501 }
502
503 break;
504
505 case STATE_SIGNAL:
506
507 if (t == XML_ATTRIBUTE_NAME) {
508 if (streq_ptr(name, "name"))
509 state = STATE_SIGNAL_NAME;
510 else {
511 log_error("Unexpected <signal> attribute %s.", name);
512 return -EBADMSG;
513 }
514 } else if (t == XML_TAG_OPEN) {
515 if (streq_ptr(name, "arg"))
516 state = STATE_SIGNAL_ARG;
517 else if (streq_ptr(name, "annotation")) {
518 r = parse_xml_annotation(context, &context->member_flags);
519 if (r < 0)
520 return r;
521 } else {
522 log_error("Unexpected <signal> tag %s.", name);
523 return -EINVAL;
524 }
525 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
526 (t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) {
527
528 if (n_depth == 0) {
529 if (context->ops->on_signal) {
530 r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata);
531 if (r < 0)
532 return r;
533 }
534
535 context_reset_member(context);
536 }
a1ad3767
LP
537
538 state = STATE_INTERFACE;
539
0171da06 540 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
541 log_error("Unexpected token in <signal>. (1)");
542 return -EINVAL;
543 }
544
545 break;
546
547 case STATE_SIGNAL_NAME:
548
0171da06 549 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef
MM
550 if (n_depth == 0)
551 free_and_replace(context->member_name, name);
0171da06 552
a1ad3767 553 state = STATE_SIGNAL;
0171da06 554 } else {
a1ad3767
LP
555 log_error("Unexpected token in <signal>. (2)");
556 return -EINVAL;
557 }
558
559 break;
560
561
562 case STATE_SIGNAL_ARG:
563
564 if (t == XML_ATTRIBUTE_NAME) {
565 if (streq_ptr(name, "name"))
566 state = STATE_SIGNAL_ARG_NAME;
567 else if (streq_ptr(name, "type"))
568 state = STATE_SIGNAL_ARG_TYPE;
874899e1
MM
569 else if (streq_ptr(name, "direction"))
570 state = STATE_SIGNAL_ARG_DIRECTION;
a1ad3767
LP
571 else {
572 log_error("Unexpected signal <arg> attribute %s.", name);
573 return -EBADMSG;
574 }
575 } else if (t == XML_TAG_OPEN) {
576 if (streq_ptr(name, "annotation")) {
577 r = parse_xml_annotation(context, NULL);
578 if (r < 0)
579 return r;
580 } else {
581 log_error("Unexpected signal <arg> tag %s.", name);
582 return -EINVAL;
583 }
584 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06 585 (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
a1ad3767 586
0171da06 587 if (argument_type) {
874899e1
MM
588 if (!argument_direction || streq(argument_direction, "out")) {
589 if (!strextend(&context->member_signature, argument_type, NULL))
590 return log_oom();
591 } else
592 log_error("Unexpected signal <arg> direction value '%s'.", argument_direction);
a1ad3767 593
97b11eed 594 argument_type = mfree(argument_type);
0171da06
LP
595 }
596
597 state = STATE_SIGNAL;
598 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
599 log_error("Unexpected token in signal <arg> (1).");
600 return -EINVAL;
601 }
602
603 break;
604
605 case STATE_SIGNAL_ARG_NAME:
606
607 if (t == XML_ATTRIBUTE_VALUE)
608 state = STATE_SIGNAL_ARG;
609 else {
610 log_error("Unexpected token in signal <arg> (2).");
611 return -EINVAL;
612 }
613
614 break;
615
616 case STATE_SIGNAL_ARG_TYPE:
617
0171da06 618 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef 619 free_and_replace(argument_type, name);
0171da06 620
a1ad3767 621 state = STATE_SIGNAL_ARG;
0171da06 622 } else {
a1ad3767
LP
623 log_error("Unexpected token in signal <arg> (3).");
624 return -EINVAL;
625 }
626
627 break;
628
874899e1
MM
629 case STATE_SIGNAL_ARG_DIRECTION:
630
631 if (t == XML_ATTRIBUTE_VALUE) {
632 free_and_replace(argument_direction, name);
633
634 state = STATE_SIGNAL_ARG;
635 } else {
636 log_error("Unexpected token in signal <arg>. (4)");
637 return -EINVAL;
638 }
639
640 break;
641
a1ad3767
LP
642 case STATE_PROPERTY:
643
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;
651 else {
652 log_error("Unexpected <property> attribute %s.", name);
653 return -EBADMSG;
654 }
655 } else if (t == XML_TAG_OPEN) {
656
657 if (streq_ptr(name, "annotation")) {
658 r = parse_xml_annotation(context, &context->member_flags);
659 if (r < 0)
660 return r;
661 } else {
662 log_error("Unexpected <property> tag %s.", name);
663 return -EINVAL;
664 }
665
666 } else if (t == XML_TAG_CLOSE_EMPTY ||
0171da06
LP
667 (t == XML_TAG_CLOSE && streq_ptr(name, "property"))) {
668
669 if (n_depth == 0) {
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);
672 if (r < 0)
673 return r;
674 }
675
676 context_reset_member(context);
677 }
a1ad3767
LP
678
679 state = STATE_INTERFACE;
680
0171da06 681 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
a1ad3767
LP
682 log_error("Unexpected token in <property>. (1)");
683 return -EINVAL;
684 }
685
686 break;
687
688 case STATE_PROPERTY_NAME:
689
0171da06 690 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef
MM
691 if (n_depth == 0)
692 free_and_replace(context->member_name, name);
0171da06 693
a1ad3767 694 state = STATE_PROPERTY;
0171da06 695 } else {
a1ad3767
LP
696 log_error("Unexpected token in <property>. (2)");
697 return -EINVAL;
698 }
699
700 break;
701
702 case STATE_PROPERTY_TYPE:
703
0171da06 704 if (t == XML_ATTRIBUTE_VALUE) {
057a32ef
MM
705 if (n_depth == 0)
706 free_and_replace(context->member_signature, name);
0171da06 707
a1ad3767 708 state = STATE_PROPERTY;
0171da06 709 } else {
a1ad3767
LP
710 log_error("Unexpected token in <property>. (3)");
711 return -EINVAL;
712 }
713
714 break;
715
716 case STATE_PROPERTY_ACCESS:
717
0171da06
LP
718 if (t == XML_ATTRIBUTE_VALUE) {
719
720 if (streq(name, "readwrite") || streq(name, "write"))
721 context->member_writable = true;
722
a1ad3767 723 state = STATE_PROPERTY;
0171da06 724 } else {
a1ad3767
LP
725 log_error("Unexpected token in <property>. (4)");
726 return -EINVAL;
727 }
728
729 break;
730 }
731 }
732}
733
734int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) {
735 Context context = {
736 .ops = ops,
737 .userdata = userdata,
738 .current = xml,
739 };
740
741 int r;
742
743 assert(prefix);
744 assert(xml);
745 assert(ops);
746
747 for (;;) {
748 _cleanup_free_ char *name = NULL;
749
750 r = xml_tokenize(&context.current, &name, &context.xml_state, NULL);
751 if (r < 0) {
752 log_error("XML parse error");
0171da06 753 goto finish;
a1ad3767
LP
754 }
755
0171da06
LP
756 if (r == XML_END) {
757 r = 0;
a1ad3767 758 break;
0171da06 759 }
a1ad3767
LP
760
761 if (r == XML_TAG_OPEN) {
762
763 if (streq(name, "node")) {
764 r = parse_xml_node(&context, prefix, 0);
765 if (r < 0)
0171da06 766 goto finish;
a1ad3767
LP
767 } else {
768 log_error("Unexpected tag '%s' in introspection data.", name);
0171da06
LP
769 r = -EBADMSG;
770 goto finish;
a1ad3767
LP
771 }
772 } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
773 log_error("Unexpected token.");
0171da06
LP
774 r = -EBADMSG;
775 goto finish;
a1ad3767
LP
776 }
777 }
778
0171da06
LP
779finish:
780 context_reset_interface(&context);
781
782 return r;
a1ad3767 783}