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