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