pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
xpath.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11#include <stdio.h>
12#include <string.h>
13#include <crm/common/xml.h>
15#include "crmcommon_private.h"
16
17/*
18 * From xpath2.c
19 *
20 * All the elements returned by an XPath query are pointers to
21 * elements from the tree *except* namespace nodes where the XPath
22 * semantic is different from the implementation in libxml2 tree.
23 * As a result when a returned node set is freed when
24 * xmlXPathFreeObject() is called, that routine must check the
25 * element type. But node from the returned set may have been removed
26 * by xmlNodeSetContent() resulting in access to freed data.
27 *
28 * This can be exercised by running
29 * valgrind xpath2 test3.xml '//discarded' discarded
30 *
31 * There is 2 ways around it:
32 * - make a copy of the pointers to the nodes from the result set
33 * then call xmlXPathFreeObject() and then modify the nodes
34 * or
35 * - remove the references from the node set, if they are not
36 namespace nodes, before calling xmlXPathFreeObject().
37 */
38void
39freeXpathObject(xmlXPathObjectPtr xpathObj)
40{
41 int lpc, max = numXpathResults(xpathObj);
42
43 if (xpathObj == NULL) {
44 return;
45 }
46
47 for (lpc = 0; lpc < max; lpc++) {
48 if (xpathObj->nodesetval->nodeTab[lpc] && xpathObj->nodesetval->nodeTab[lpc]->type != XML_NAMESPACE_DECL) {
49 xpathObj->nodesetval->nodeTab[lpc] = NULL;
50 }
51 }
52
53 /* _Now_ it's safe to free it */
54 xmlXPathFreeObject(xpathObj);
55}
56
57xmlNode *
58getXpathResult(xmlXPathObjectPtr xpathObj, int index)
59{
60 xmlNode *match = NULL;
61 int max = numXpathResults(xpathObj);
62
63 CRM_CHECK(index >= 0, return NULL);
64 CRM_CHECK(xpathObj != NULL, return NULL);
65
66 if (index >= max) {
67 crm_err("Requested index %d of only %d items", index, max);
68 return NULL;
69
70 } else if(xpathObj->nodesetval->nodeTab[index] == NULL) {
71 /* Previously requested */
72 return NULL;
73 }
74
75 match = xpathObj->nodesetval->nodeTab[index];
76 CRM_CHECK(match != NULL, return NULL);
77
78 if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
79 /* See the comment for freeXpathObject() */
80 xpathObj->nodesetval->nodeTab[index] = NULL;
81 }
82
83 if (match->type == XML_DOCUMENT_NODE) {
84 /* Will happen if section = '/' */
85 match = match->children;
86
87 } else if (match->type != XML_ELEMENT_NODE
88 && match->parent && match->parent->type == XML_ELEMENT_NODE) {
89 /* Return the parent instead */
90 match = match->parent;
91
92 } else if (match->type != XML_ELEMENT_NODE) {
93 /* We only support searching nodes */
94 crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type);
95 match = NULL;
96 }
97 return match;
98}
99
100void
101dedupXpathResults(xmlXPathObjectPtr xpathObj)
102{
103 int lpc, max = numXpathResults(xpathObj);
104
105 if (xpathObj == NULL) {
106 return;
107 }
108
109 for (lpc = 0; lpc < max; lpc++) {
110 xmlNode *xml = NULL;
111 gboolean dedup = FALSE;
112
113 if (xpathObj->nodesetval->nodeTab[lpc] == NULL) {
114 continue;
115 }
116
117 xml = xpathObj->nodesetval->nodeTab[lpc]->parent;
118
119 for (; xml; xml = xml->parent) {
120 int lpc2 = 0;
121
122 for (lpc2 = 0; lpc2 < max; lpc2++) {
123 if (xpathObj->nodesetval->nodeTab[lpc2] == xml) {
124 xpathObj->nodesetval->nodeTab[lpc] = NULL;
125 dedup = TRUE;
126 break;
127 }
128 }
129
130 if (dedup) {
131 break;
132 }
133 }
134 }
135}
136
137/* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
138xmlXPathObjectPtr
139xpath_search(const xmlNode *xml_top, const char *path)
140{
141 xmlXPathObjectPtr xpathObj = NULL;
142 xmlXPathContextPtr xpathCtx = NULL;
143 const xmlChar *xpathExpr = (pcmkXmlStr) path;
144
145 CRM_CHECK(path != NULL, return NULL);
146 CRM_CHECK(xml_top != NULL, return NULL);
147 CRM_CHECK(strlen(path) > 0, return NULL);
148
149 xpathCtx = xmlXPathNewContext(xml_top->doc);
150 pcmk__mem_assert(xpathCtx);
151
152 xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
153 xmlXPathFreeContext(xpathCtx);
154 return xpathObj;
155}
156
169void
170crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
171 void (*helper)(xmlNode*, void*), void *user_data)
172{
173 xmlXPathObjectPtr xpathObj = xpath_search(xml, xpath);
174 int nresults = numXpathResults(xpathObj);
175 int i;
176
177 for (i = 0; i < nresults; i++) {
178 xmlNode *result = getXpathResult(xpathObj, i);
179
180 CRM_LOG_ASSERT(result != NULL);
181 if (result) {
182 (*helper)(result, user_data);
183 }
184 }
185 freeXpathObject(xpathObj);
186}
187
188xmlNode *
189get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level)
190{
191 int max;
192 xmlNode *result = NULL;
193 xmlXPathObjectPtr xpathObj = NULL;
194 char *nodePath = NULL;
195 char *matchNodePath = NULL;
196
197 if (xpath == NULL) {
198 return xml_obj; /* or return NULL? */
199 }
200
201 xpathObj = xpath_search(xml_obj, xpath);
202 nodePath = (char *)xmlGetNodePath(xml_obj);
203 max = numXpathResults(xpathObj);
204
205 if (max < 1) {
206 if (error_level < LOG_NEVER) {
207 do_crm_log(error_level, "No match for %s in %s",
208 xpath, pcmk__s(nodePath, "unknown path"));
209 crm_log_xml_explicit(xml_obj, "Unexpected Input");
210 }
211
212 } else if (max > 1) {
213 if (error_level < LOG_NEVER) {
214 int lpc = 0;
215
216 do_crm_log(error_level, "Too many matches for %s in %s",
217 xpath, pcmk__s(nodePath, "unknown path"));
218
219 for (lpc = 0; lpc < max; lpc++) {
220 xmlNode *match = getXpathResult(xpathObj, lpc);
221
222 CRM_LOG_ASSERT(match != NULL);
223 if (match != NULL) {
224 matchNodePath = (char *) xmlGetNodePath(match);
225 do_crm_log(error_level, "%s[%d] = %s",
226 xpath, lpc,
227 pcmk__s(matchNodePath, "unrecognizable match"));
228 free(matchNodePath);
229 }
230 }
231 crm_log_xml_explicit(xml_obj, "Bad Input");
232 }
233
234 } else {
235 result = getXpathResult(xpathObj, 0);
236 }
237
238 freeXpathObject(xpathObj);
239 free(nodePath);
240
241 return result;
242}
243
255GString *
256pcmk__element_xpath(const xmlNode *xml)
257{
258 const xmlNode *parent = NULL;
259 GString *xpath = NULL;
260 const char *id = NULL;
261
262 if (xml == NULL) {
263 return NULL;
264 }
265
266 parent = xml->parent;
268 if (xpath == NULL) {
269 xpath = g_string_sized_new(256);
270 }
271
272 // Build xpath like "/" -> "/cib" -> "/cib/configuration"
273 if (parent == NULL) {
274 g_string_append_c(xpath, '/');
275 } else if (parent->parent == NULL) {
276 g_string_append(xpath, (const gchar *) xml->name);
277 } else {
278 pcmk__g_strcat(xpath, "/", (const char *) xml->name, NULL);
279 }
280
281 id = pcmk__xe_id(xml);
282 if (id != NULL) {
283 pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", id, "']", NULL);
284 }
285
286 return xpath;
287}
288
289char *
290pcmk__xpath_node_id(const char *xpath, const char *node)
291{
292 char *retval = NULL;
293 char *patt = NULL;
294 char *start = NULL;
295 char *end = NULL;
296
297 if (node == NULL || xpath == NULL) {
298 return retval;
299 }
300
301 patt = crm_strdup_printf("/%s[@" PCMK_XA_ID "=", node);
302 start = strstr(xpath, patt);
303
304 if (!start) {
305 free(patt);
306 return retval;
307 }
308
309 start += strlen(patt);
310 start++;
311
312 end = strstr(start, "\'");
313 CRM_ASSERT(end);
314 retval = strndup(start, end-start);
315
316 free(patt);
317 return retval;
318}
319
320static int
321output_attr_child(xmlNode *child, void *userdata)
322{
323 pcmk__output_t *out = userdata;
324
325 out->info(out, " Value: %s \t(id=%s)",
327 pcmk__s(pcmk__xe_id(child), "<none>"));
328 return pcmk_rc_ok;
329}
330
331void
333 const char *name)
334{
335 if (out == NULL || name == NULL || search == NULL ||
336 search->children == NULL) {
337 return;
338 }
339
340 out->info(out, "Multiple attributes match " PCMK_XA_NAME "=%s", name);
341 pcmk__xe_foreach_child(search, NULL, output_attr_child, out);
342}
343
344// Deprecated functions kept only for backward API compatibility
345// LCOV_EXCL_START
346
348
359char *
360xml_get_path(const xmlNode *xml)
361{
362 char *path = NULL;
363 GString *g_path = pcmk__element_xpath(xml);
364
365 if (g_path == NULL) {
366 return NULL;
367 }
368 path = pcmk__str_copy(g_path->str);
369 g_string_free(g_path, TRUE);
370 return path;
371}
372
373xmlNode *
374get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level)
375{
376 xmlNode *result = NULL;
377 char *xpath_full = NULL;
378 char *xpath_prefix = NULL;
379
380 if (xml_obj == NULL || xpath == NULL) {
381 return NULL;
382 }
383
384 xpath_prefix = (char *)xmlGetNodePath(xml_obj);
385
386 xpath_full = crm_strdup_printf("%s%s", xpath_prefix, xpath);
387
388 result = get_xpath_object(xpath_full, xml_obj, error_level);
389
390 free(xpath_prefix);
391 free(xpath_full);
392 return result;
393}
394
395// LCOV_EXCL_STOP
396// End deprecated API
const char * parent
Definition cib.c:27
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define do_crm_log(level, fmt, args...)
Log a message.
Definition logging.h:181
#define CRM_LOG_ASSERT(expr)
Definition logging.h:228
#define crm_log_xml_explicit(xml, text)
Definition logging.h:412
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_err(fmt, args...)
Definition logging.h:389
#define LOG_NEVER
Definition logging.h:48
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
pcmk__action_result_t result
Definition pcmk_fence.c:35
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
#define pcmk__mem_assert(ptr)
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1296
#define pcmk__str_copy(str)
This structure contains everything that makes up a single output formatter.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Wrappers for and extensions to libxml2.
const xmlChar * pcmkXmlStr
Definition xml.h:41
Deprecated Pacemaker XML API.
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition xml.c:2288
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_VALUE
Definition xml_names.h:437
#define PCMK_XA_NAME
Definition xml_names.h:325
void dedupXpathResults(xmlXPathObjectPtr xpathObj)
Definition xpath.c:101
void crm_foreach_xpath_result(xmlNode *xml, const char *xpath, void(*helper)(xmlNode *, void *), void *user_data)
Run a supplied function for each result of an xpath search.
Definition xpath.c:170
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition xpath.c:189
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition xpath.c:58
char * xml_get_path(const xmlNode *xml)
Get an XPath string that matches an XML element as closely as possible.
Definition xpath.c:360
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition xpath.c:39
void pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search, const char *name)
Definition xpath.c:332
GString * pcmk__element_xpath(const xmlNode *xml)
Definition xpath.c:256
xmlNode * get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level)
Definition xpath.c:374
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition xpath.c:139
char * pcmk__xpath_node_id(const char *xpath, const char *node)
Definition xpath.c:290