]>
Commit | Line | Data |
---|---|---|
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 | ||
13 | typedef 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 | ||
30 | static 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 | ||
40 | static 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 | ||
47 | static 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 | ||
154 | static 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 | ||
683 | int 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 | ||
728 | finish: | |
729 | context_reset_interface(&context); | |
730 | ||
731 | return r; | |
732 | } |