This source file includes following definitions.
- pcmk__xpath_result
- pcmk__xpath_match_element
- pcmk__xpath_search
- pcmk__xpath_foreach_result
- pcmk__xpath_find_one
- pcmk__element_xpath
- pcmk__xpath_node_id
- output_attr_child
- pcmk__warn_multiple_name_matches
- xpath_search
- getXpathResult
- freeXpathObject
- dedupXpathResults
- crm_foreach_xpath_result
- get_xpath_object
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <string.h>
14
15 #include <libxml/tree.h>
16 #include <libxml/xmlstring.h>
17 #include <libxml/xpath.h>
18
19 #include <crm/common/xml.h>
20 #include <crm/common/xml_internal.h>
21 #include "crmcommon_private.h"
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 xmlNode *
65 pcmk__xpath_result(xmlXPathObject *xpath_obj, int index)
66 {
67 xmlNode *match = NULL;
68
69 CRM_CHECK((xpath_obj != NULL) && (index >= 0), return NULL);
70
71 match = xmlXPathNodeSetItem(xpath_obj->nodesetval, index);
72 if (match == NULL) {
73
74 return NULL;
75 }
76
77 if (match->type != XML_NAMESPACE_DECL) {
78 xpath_obj->nodesetval->nodeTab[index] = NULL;
79 }
80
81 return match;
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 xmlNode *
102 pcmk__xpath_match_element(xmlNode *match)
103 {
104 pcmk__assert(match != NULL);
105
106 switch (match->type) {
107 case XML_ELEMENT_NODE:
108 return match;
109
110 case XML_DOCUMENT_NODE:
111
112 return xmlDocGetRootElement((xmlDoc *) match);
113
114 default:
115 if ((match->parent != NULL)
116 && (match->parent->type == XML_ELEMENT_NODE)) {
117
118
119 return match->parent;
120 }
121 crm_err("Cannot get element from XPath expression match of type %s",
122 pcmk__xml_element_type_text(match->type));
123 return NULL;
124 }
125 }
126
127
128
129
130
131
132
133
134
135
136 xmlXPathObject *
137 pcmk__xpath_search(xmlDoc *doc, const char *path)
138 {
139 const xmlChar *xpath_expr = (const xmlChar *) path;
140 xmlXPathContext *xpath_context = NULL;
141 xmlXPathObject *xpath_obj = NULL;
142
143 CRM_CHECK((doc != NULL) && !pcmk__str_empty(path), return NULL);
144
145 xpath_context = xmlXPathNewContext(doc);
146 pcmk__mem_assert(xpath_context);
147
148 xpath_obj = xmlXPathEval(xpath_expr, xpath_context);
149
150 xmlXPathFreeContext(xpath_context);
151 return xpath_obj;
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 void
170 pcmk__xpath_foreach_result(xmlDoc *doc, const char *path,
171 void (*fn)(xmlNode *, void *), void *user_data)
172 {
173 xmlXPathObject *xpath_obj = NULL;
174 int num_results = 0;
175
176 CRM_CHECK((doc != NULL) && !pcmk__str_empty(path) && (fn != NULL), return);
177
178 xpath_obj = pcmk__xpath_search(doc, path);
179 num_results = pcmk__xpath_num_results(xpath_obj);
180
181 for (int i = 0; i < num_results; i++) {
182 xmlNode *result = pcmk__xpath_result(xpath_obj, i);
183
184 if (result != NULL) {
185 (*fn)(result, user_data);
186 }
187 }
188 xmlXPathFreeObject(xpath_obj);
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 xmlNode *
206 pcmk__xpath_find_one(xmlDoc *doc, const char *path, uint8_t level)
207 {
208 int num_results = 0;
209 xmlNode *result = NULL;
210 xmlXPathObject *xpath_obj = NULL;
211 const xmlNode *root = NULL;
212 const char *root_name = "(unknown)";
213
214 CRM_CHECK((doc != NULL) && (path != NULL), goto done);
215
216 xpath_obj = pcmk__xpath_search(doc, path);
217 num_results = pcmk__xpath_num_results(xpath_obj);
218
219 if (num_results == 1) {
220 result = pcmk__xpath_result(xpath_obj, 0);
221 goto done;
222 }
223
224 if (level >= LOG_NEVER) {
225
226 goto done;
227 }
228
229 root = xmlDocGetRootElement(doc);
230 if (root != NULL) {
231 root_name = (const char *) root->name;
232 }
233
234 if (num_results < 1) {
235 do_crm_log(level, "No match for %s in <%s>", path, root_name);
236
237 if (root != NULL) {
238 crm_log_xml_explicit(root, "no-match");
239 }
240 goto done;
241 }
242
243 do_crm_log(level, "Multiple matches for %s in <%s>", path, root_name);
244
245 for (int i = 0; i < num_results; i++) {
246 xmlNode *match = pcmk__xpath_result(xpath_obj, i);
247 xmlChar *match_path = NULL;
248
249 if (match == NULL) {
250 CRM_LOG_ASSERT(match != NULL);
251 continue;
252 }
253
254 match_path = xmlGetNodePath(match);
255 do_crm_log(level, "%s[%d] = %s",
256 path, i, pcmk__s((const char *) match_path, "(unknown)"));
257 free(match_path);
258 }
259
260 if (root != NULL) {
261 crm_log_xml_explicit(root, "multiple-matches");
262 }
263
264 done:
265 xmlXPathFreeObject(xpath_obj);
266 return result;
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280 GString *
281 pcmk__element_xpath(const xmlNode *xml)
282 {
283 const xmlNode *parent = NULL;
284 GString *xpath = NULL;
285 const char *id = NULL;
286
287 if (xml == NULL) {
288 return NULL;
289 }
290
291 parent = xml->parent;
292 xpath = pcmk__element_xpath(parent);
293 if (xpath == NULL) {
294 xpath = g_string_sized_new(256);
295 }
296
297
298 if (parent == NULL) {
299 g_string_append_c(xpath, '/');
300 } else if (parent->parent == NULL) {
301 g_string_append(xpath, (const gchar *) xml->name);
302 } else {
303 pcmk__g_strcat(xpath, "/", (const char *) xml->name, NULL);
304 }
305
306 id = pcmk__xe_id(xml);
307 if (id != NULL) {
308 pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", id, "']", NULL);
309 }
310
311 return xpath;
312 }
313
314
315
316
317
318
319
320
321
322
323 char *
324 pcmk__xpath_node_id(const char *xpath, const char *node)
325 {
326 char *retval = NULL;
327 char *patt = NULL;
328 char *start = NULL;
329 char *end = NULL;
330
331 if (node == NULL || xpath == NULL) {
332 return retval;
333 }
334
335 patt = crm_strdup_printf("/%s[@" PCMK_XA_ID "=", node);
336 start = strstr(xpath, patt);
337
338 if (!start) {
339 free(patt);
340 return retval;
341 }
342
343 start += strlen(patt);
344 start++;
345
346 end = strstr(start, "\'");
347 pcmk__assert(end != NULL);
348 retval = strndup(start, end-start);
349
350 free(patt);
351 return retval;
352 }
353
354 static int
355 output_attr_child(xmlNode *child, void *userdata)
356 {
357 pcmk__output_t *out = userdata;
358
359 out->info(out, " Value: %s \t(id=%s)",
360 crm_element_value(child, PCMK_XA_VALUE),
361 pcmk__s(pcmk__xe_id(child), "<none>"));
362 return pcmk_rc_ok;
363 }
364
365
366
367
368
369
370
371
372
373
374 void
375 pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search,
376 const char *name)
377 {
378 if (out == NULL || name == NULL || search == NULL ||
379 search->children == NULL) {
380 return;
381 }
382
383 out->info(out, "Multiple attributes match " PCMK_XA_NAME "=%s", name);
384 pcmk__xe_foreach_child(search, NULL, output_attr_child, out);
385 }
386
387
388
389
390 #include <crm/common/xml_compat.h>
391
392 xmlXPathObjectPtr
393 xpath_search(const xmlNode *xml_top, const char *path)
394 {
395 CRM_CHECK(xml_top != NULL, return NULL);
396
397 return pcmk__xpath_search(xml_top->doc, path);
398 }
399
400 xmlNode *
401 getXpathResult(xmlXPathObjectPtr xpathObj, int index)
402 {
403 xmlNode *match = NULL;
404 int max = pcmk__xpath_num_results(xpathObj);
405
406 CRM_CHECK(index >= 0, return NULL);
407 CRM_CHECK(xpathObj != NULL, return NULL);
408
409 if (index >= max) {
410 crm_err("Requested index %d of only %d items", index, max);
411 return NULL;
412
413 } else if(xpathObj->nodesetval->nodeTab[index] == NULL) {
414
415 return NULL;
416 }
417
418 match = xpathObj->nodesetval->nodeTab[index];
419 CRM_CHECK(match != NULL, return NULL);
420
421 if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
422
423 xpathObj->nodesetval->nodeTab[index] = NULL;
424 }
425
426 switch (match->type) {
427 case XML_ELEMENT_NODE:
428 return match;
429
430 case XML_DOCUMENT_NODE:
431 return match->children;
432
433 default:
434 if ((match->parent != NULL)
435 && (match->parent->type == XML_ELEMENT_NODE)) {
436 return match->parent;
437 }
438 crm_warn("Unsupported XPath match type %d (bug?)", match->type);
439 return NULL;
440 }
441 }
442
443 void
444 freeXpathObject(xmlXPathObjectPtr xpathObj)
445 {
446 int max = pcmk__xpath_num_results(xpathObj);
447
448 if (xpathObj == NULL) {
449 return;
450 }
451
452 for (int lpc = 0; lpc < max; lpc++) {
453 if (xpathObj->nodesetval->nodeTab[lpc] && xpathObj->nodesetval->nodeTab[lpc]->type != XML_NAMESPACE_DECL) {
454 xpathObj->nodesetval->nodeTab[lpc] = NULL;
455 }
456 }
457
458
459 xmlXPathFreeObject(xpathObj);
460 }
461
462 void
463 dedupXpathResults(xmlXPathObjectPtr xpathObj)
464 {
465 int max = pcmk__xpath_num_results(xpathObj);
466
467 if (xpathObj == NULL) {
468 return;
469 }
470
471 for (int lpc = 0; lpc < max; lpc++) {
472 xmlNode *xml = NULL;
473 gboolean dedup = FALSE;
474
475 if (xpathObj->nodesetval->nodeTab[lpc] == NULL) {
476 continue;
477 }
478
479 xml = xpathObj->nodesetval->nodeTab[lpc]->parent;
480
481 for (; xml; xml = xml->parent) {
482 int lpc2 = 0;
483
484 for (lpc2 = 0; lpc2 < max; lpc2++) {
485 if (xpathObj->nodesetval->nodeTab[lpc2] == xml) {
486 xpathObj->nodesetval->nodeTab[lpc] = NULL;
487 dedup = TRUE;
488 break;
489 }
490 }
491
492 if (dedup) {
493 break;
494 }
495 }
496 }
497 }
498
499 void
500 crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
501 void (*helper)(xmlNode*, void*), void *user_data)
502 {
503 xmlXPathObject *xpathObj = NULL;
504 int nresults = 0;
505
506 CRM_CHECK(xml != NULL, return);
507
508 xpathObj = pcmk__xpath_search(xml->doc, xpath);
509 nresults = pcmk__xpath_num_results(xpathObj);
510
511 for (int i = 0; i < nresults; i++) {
512 xmlNode *result = pcmk__xpath_result(xpathObj, i);
513
514 CRM_LOG_ASSERT(result != NULL);
515
516 if (result != NULL) {
517 result = pcmk__xpath_match_element(result);
518
519 CRM_LOG_ASSERT(result != NULL);
520
521 if (result != NULL) {
522 (*helper)(result, user_data);
523 }
524 }
525 }
526 xmlXPathFreeObject(xpathObj);
527 }
528
529 xmlNode *
530 get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level)
531 {
532 int max;
533 xmlNode *result = NULL;
534 xmlXPathObject *xpathObj = NULL;
535 char *nodePath = NULL;
536 char *matchNodePath = NULL;
537
538 if (xpath == NULL) {
539 return xml_obj;
540 }
541
542 xpathObj = pcmk__xpath_search(xml_obj->doc, xpath);
543 nodePath = (char *)xmlGetNodePath(xml_obj);
544 max = pcmk__xpath_num_results(xpathObj);
545
546 if (max == 0) {
547 if (error_level < LOG_NEVER) {
548 do_crm_log(error_level, "No match for %s in %s",
549 xpath, pcmk__s(nodePath, "unknown path"));
550 crm_log_xml_explicit(xml_obj, "Unexpected Input");
551 }
552
553 } else if (max > 1) {
554 if (error_level < LOG_NEVER) {
555 int lpc = 0;
556
557 do_crm_log(error_level, "Too many matches for %s in %s",
558 xpath, pcmk__s(nodePath, "unknown path"));
559
560 for (lpc = 0; lpc < max; lpc++) {
561 xmlNode *match = pcmk__xpath_result(xpathObj, lpc);
562
563 CRM_LOG_ASSERT(match != NULL);
564 if (match != NULL) {
565 match = pcmk__xpath_match_element(match);
566
567 CRM_LOG_ASSERT(match != NULL);
568 if (match != NULL) {
569 matchNodePath = (char *) xmlGetNodePath(match);
570 do_crm_log(error_level, "%s[%d] = %s",
571 xpath, lpc,
572 pcmk__s(matchNodePath,
573 "unrecognizable match"));
574 free(matchNodePath);
575 }
576 }
577 }
578 crm_log_xml_explicit(xml_obj, "Bad Input");
579 }
580
581 } else {
582 result = pcmk__xpath_result(xpathObj, 0);
583 if (result != NULL) {
584 result = pcmk__xpath_match_element(result);
585 }
586 }
587
588 xmlXPathFreeObject(xpathObj);
589 free(nodePath);
590
591 return result;
592 }
593
594
595