pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
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  */
38 void
39 freeXpathObject(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 
57 xmlNode *
58 getXpathResult(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 
100 void
101 dedupXpathResults(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 */
138 xmlXPathObjectPtr
139 xpath_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 
169 void
170 crm_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 
188 xmlNode *
189 get_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 
255 GString *
256 pcmk__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;
267  xpath = pcmk__element_xpath(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 
289 char *
290 pcmk__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 
320 static int
321 output_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 
331 void
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 
347 #include <crm/common/xml_compat.h>
348 
359 char *
360 xml_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 
373 xmlNode *
374 get_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
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
#define PCMK_XA_NAME
Definition: xml_names.h:325
const char * name
Definition: cib.c:26
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
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:39
xmlNode * get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:374
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:228
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
#define LOG_NEVER
Definition: logging.h:48
Deprecated Pacemaker XML API.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:181
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1296
#define crm_log_xml_explicit(xml, text)
Definition: logging.h:414
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition: xpath.c:58
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Wrappers for and extensions to libxml2.
#define PCMK_XA_ID
Definition: xml_names.h:296
#define PCMK_XA_VALUE
Definition: xml_names.h:437
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition: xpath.c:139
#define pcmk__str_copy(str)
char * xml_get_path(const xmlNode *xml)
Get an XPath string that matches an XML element as closely as possible.
Definition: xpath.c:360
const xmlChar * pcmkXmlStr
Definition: xml.h:41
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:189
void dedupXpathResults(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:101
char * pcmk__xpath_node_id(const char *xpath, const char *node)
Definition: xpath.c:290
pcmk__action_result_t result
Definition: pcmk_fence.c:35
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:2288
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:391
#define CRM_ASSERT(expr)
Definition: results.h:42
#define pcmk__mem_assert(ptr)
This structure contains everything that makes up a single output formatter.
void pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search, const char *name)
Definition: xpath.c:332
const char * parent
Definition: cib.c:27
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:256