root/lib/common/xpath.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. freeXpathObject
  2. getXpathResult
  3. dedupXpathResults
  4. xpath_search
  5. crm_foreach_xpath_result
  6. get_xpath_object_relative
  7. get_xpath_object
  8. pcmk__element_xpath
  9. xml_get_path

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 159 /*!
 160  * \brief Run a supplied function for each result of an xpath search
 161  *
 162  * \param[in] xml            XML to search
 163  * \param[in] xpath          XPath search string
 164  * \param[in] helper         Function to call for each result
 165  * \param[in,out] user_data  Data to pass to supplied function
 166  *
 167  * \note The helper function will be passed the XML node of the result,
 168  *       and the supplied user_data. This function does not otherwise
 169  *       use user_data.
 170  */
 171 void
 172 crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */