pacemaker  2.0.5-ba59be712
Scalable High-Availability cluster resource manager
xpath.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 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/msg_xml.h>
14 #include "crmcommon_private.h"
15 
16 /*
17  * From xpath2.c
18  *
19  * All the elements returned by an XPath query are pointers to
20  * elements from the tree *except* namespace nodes where the XPath
21  * semantic is different from the implementation in libxml2 tree.
22  * As a result when a returned node set is freed when
23  * xmlXPathFreeObject() is called, that routine must check the
24  * element type. But node from the returned set may have been removed
25  * by xmlNodeSetContent() resulting in access to freed data.
26  *
27  * This can be exercised by running
28  * valgrind xpath2 test3.xml '//discarded' discarded
29  *
30  * There is 2 ways around it:
31  * - make a copy of the pointers to the nodes from the result set
32  * then call xmlXPathFreeObject() and then modify the nodes
33  * or
34  * - remove the references from the node set, if they are not
35  namespace nodes, before calling xmlXPathFreeObject().
36  */
37 void
38 freeXpathObject(xmlXPathObjectPtr xpathObj)
39 {
40  int lpc, max = numXpathResults(xpathObj);
41 
42  if (xpathObj == NULL) {
43  return;
44  }
45 
46  for (lpc = 0; lpc < max; lpc++) {
47  if (xpathObj->nodesetval->nodeTab[lpc] && xpathObj->nodesetval->nodeTab[lpc]->type != XML_NAMESPACE_DECL) {
48  xpathObj->nodesetval->nodeTab[lpc] = NULL;
49  }
50  }
51 
52  /* _Now_ it's safe to free it */
53  xmlXPathFreeObject(xpathObj);
54 }
55 
56 xmlNode *
57 getXpathResult(xmlXPathObjectPtr xpathObj, int index)
58 {
59  xmlNode *match = NULL;
60  int max = numXpathResults(xpathObj);
61 
62  CRM_CHECK(index >= 0, return NULL);
63  CRM_CHECK(xpathObj != NULL, return NULL);
64 
65  if (index >= max) {
66  crm_err("Requested index %d of only %d items", index, max);
67  return NULL;
68 
69  } else if(xpathObj->nodesetval->nodeTab[index] == NULL) {
70  /* Previously requested */
71  return NULL;
72  }
73 
74  match = xpathObj->nodesetval->nodeTab[index];
75  CRM_CHECK(match != NULL, return NULL);
76 
77  if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
78  /* See the comment for freeXpathObject() */
79  xpathObj->nodesetval->nodeTab[index] = NULL;
80  }
81 
82  if (match->type == XML_DOCUMENT_NODE) {
83  /* Will happen if section = '/' */
84  match = match->children;
85 
86  } else if (match->type != XML_ELEMENT_NODE
87  && match->parent && match->parent->type == XML_ELEMENT_NODE) {
88  /* Return the parent instead */
89  match = match->parent;
90 
91  } else if (match->type != XML_ELEMENT_NODE) {
92  /* We only support searching nodes */
93  crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type);
94  match = NULL;
95  }
96  return match;
97 }
98 
99 void
100 dedupXpathResults(xmlXPathObjectPtr xpathObj)
101 {
102  int lpc, max = numXpathResults(xpathObj);
103 
104  if (xpathObj == NULL) {
105  return;
106  }
107 
108  for (lpc = 0; lpc < max; lpc++) {
109  xmlNode *xml = NULL;
110  gboolean dedup = FALSE;
111 
112  if (xpathObj->nodesetval->nodeTab[lpc] == NULL) {
113  continue;
114  }
115 
116  xml = xpathObj->nodesetval->nodeTab[lpc]->parent;
117 
118  for (; xml; xml = xml->parent) {
119  int lpc2 = 0;
120 
121  for (lpc2 = 0; lpc2 < max; lpc2++) {
122  if (xpathObj->nodesetval->nodeTab[lpc2] == xml) {
123  xpathObj->nodesetval->nodeTab[lpc] = NULL;
124  dedup = TRUE;
125  break;
126  }
127  }
128 
129  if (dedup) {
130  break;
131  }
132  }
133  }
134 }
135 
136 /* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
137 xmlXPathObjectPtr
138 xpath_search(xmlNode * xml_top, const char *path)
139 {
140  xmlDocPtr doc = NULL;
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  doc = getDocPtr(xml_top);
150 
151  xpathCtx = xmlXPathNewContext(doc);
152  CRM_ASSERT(xpathCtx != NULL);
153 
154  xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
155  xmlXPathFreeContext(xpathCtx);
156  return xpathObj;
157 }
158 
171 void
172 crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
173  void (*helper)(xmlNode*, void*), void *user_data)
174 {
175  xmlXPathObjectPtr xpathObj = xpath_search(xml, xpath);
176  int nresults = numXpathResults(xpathObj);
177  int i;
178 
179  for (i = 0; i < nresults; i++) {
180  xmlNode *result = getXpathResult(xpathObj, i);
181 
182  CRM_LOG_ASSERT(result != NULL);
183  if (result) {
184  (*helper)(result, user_data);
185  }
186  }
187  freeXpathObject(xpathObj);
188 }
189 
190 xmlNode *
191 get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level)
192 {
193  xmlNode *result = NULL;
194  char *xpath_full = NULL;
195  char *xpath_prefix = NULL;
196 
197  if (xml_obj == NULL || xpath == NULL) {
198  return NULL;
199  }
200 
201  xpath_prefix = (char *)xmlGetNodePath(xml_obj);
202 
203  xpath_full = crm_strdup_printf("%s%s", xpath_prefix, xpath);
204 
205  result = get_xpath_object(xpath_full, xml_obj, error_level);
206 
207  free(xpath_prefix);
208  free(xpath_full);
209  return result;
210 }
211 
212 xmlNode *
213 get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level)
214 {
215  int max;
216  xmlNode *result = NULL;
217  xmlXPathObjectPtr xpathObj = NULL;
218  char *nodePath = NULL;
219  char *matchNodePath = NULL;
220 
221  if (xpath == NULL) {
222  return xml_obj; /* or return NULL? */
223  }
224 
225  xpathObj = xpath_search(xml_obj, xpath);
226  nodePath = (char *)xmlGetNodePath(xml_obj);
227  max = numXpathResults(xpathObj);
228 
229  if (max < 1) {
230  if (error_level < LOG_NEVER) {
231  do_crm_log(error_level, "No match for %s in %s",
232  xpath, crm_str(nodePath));
233  crm_log_xml_explicit(xml_obj, "Unexpected Input");
234  }
235 
236  } else if (max > 1) {
237  if (error_level < LOG_NEVER) {
238  int lpc = 0;
239 
240  do_crm_log(error_level, "Too many matches for %s in %s",
241  xpath, crm_str(nodePath));
242 
243  for (lpc = 0; lpc < max; lpc++) {
244  xmlNode *match = getXpathResult(xpathObj, lpc);
245 
246  CRM_LOG_ASSERT(match != NULL);
247  if (match != NULL) {
248  matchNodePath = (char *) xmlGetNodePath(match);
249  do_crm_log(error_level, "%s[%d] = %s",
250  xpath, lpc, crm_str(matchNodePath));
251  free(matchNodePath);
252  }
253  }
254  crm_log_xml_explicit(xml_obj, "Bad Input");
255  }
256 
257  } else {
258  result = getXpathResult(xpathObj, 0);
259  }
260 
261  freeXpathObject(xpathObj);
262  free(nodePath);
263 
264  return result;
265 }
266 
267 int
268 pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer,
269  int offset, size_t buffer_size)
270 {
271  const char *id = ID(xml);
272 
273  if(offset == 0 && prefix == NULL && xml->parent) {
274  offset = pcmk__element_xpath(NULL, xml->parent, buffer, offset,
275  buffer_size);
276  }
277 
278  if(id) {
279  offset += snprintf(buffer + offset, buffer_size - offset,
280  "/%s[@id='%s']", (const char *) xml->name, id);
281  } else if(xml->name) {
282  offset += snprintf(buffer + offset, buffer_size - offset,
283  "/%s", (const char *) xml->name);
284  }
285 
286  return offset;
287 }
288 
289 char *
290 xml_get_path(xmlNode *xml)
291 {
292  int offset = 0;
293  char buffer[PCMK__BUFFER_SIZE];
294 
295  if (pcmk__element_xpath(NULL, xml, buffer, offset, sizeof(buffer)) > 0) {
296  return strdup(buffer);
297  }
298  return NULL;
299 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:215
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:172
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:38
xmlNode * get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:191
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:199
#define LOG_NEVER
Definition: logging.h:46
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:625
xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path)
Definition: xpath.c:138
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:156
#define crm_log_xml_explicit(xml, text)
Definition: logging.h:363
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition: xpath.c:57
int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
Definition: xpath.c:268
const xmlChar * pcmkXmlStr
Definition: xml.h:51
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:213
void dedupXpathResults(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:100
#define PCMK__BUFFER_SIZE
char * xml_get_path(xmlNode *xml)
Definition: xpath.c:290
#define crm_err(fmt, args...)
Definition: logging.h:347
#define CRM_ASSERT(expr)
Definition: results.h:42
#define crm_str(x)
Definition: logging.h:373
#define ID(x)
Definition: msg_xml.h:425
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__