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