Branch data 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 : : }
|