LCOV - code coverage report
Current view: top level - common - xpath.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 19 149 12.8 %
Date: 2024-05-07 11:09:47 Functions: 1 10 10.0 %

          Line data    Source code
       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>
      14             : #include <crm/common/xml_internal.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           0 : freeXpathObject(xmlXPathObjectPtr xpathObj)
      40             : {
      41           0 :     int lpc, max = numXpathResults(xpathObj);
      42             : 
      43           0 :     if (xpathObj == NULL) {
      44           0 :         return;
      45             :     }
      46             : 
      47           0 :     for (lpc = 0; lpc < max; lpc++) {
      48           0 :         if (xpathObj->nodesetval->nodeTab[lpc] && xpathObj->nodesetval->nodeTab[lpc]->type != XML_NAMESPACE_DECL) {
      49           0 :             xpathObj->nodesetval->nodeTab[lpc] = NULL;
      50             :         }
      51             :     }
      52             : 
      53             :     /* _Now_ it's safe to free it */
      54           0 :     xmlXPathFreeObject(xpathObj);
      55             : }
      56             : 
      57             : xmlNode *
      58           0 : getXpathResult(xmlXPathObjectPtr xpathObj, int index)
      59             : {
      60           0 :     xmlNode *match = NULL;
      61           0 :     int max = numXpathResults(xpathObj);
      62             : 
      63           0 :     CRM_CHECK(index >= 0, return NULL);
      64           0 :     CRM_CHECK(xpathObj != NULL, return NULL);
      65             : 
      66           0 :     if (index >= max) {
      67           0 :         crm_err("Requested index %d of only %d items", index, max);
      68           0 :         return NULL;
      69             : 
      70           0 :     } else if(xpathObj->nodesetval->nodeTab[index] == NULL) {
      71             :         /* Previously requested */
      72           0 :         return NULL;
      73             :     }
      74             : 
      75           0 :     match = xpathObj->nodesetval->nodeTab[index];
      76           0 :     CRM_CHECK(match != NULL, return NULL);
      77             : 
      78           0 :     if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
      79             :         /* See the comment for freeXpathObject() */
      80           0 :         xpathObj->nodesetval->nodeTab[index] = NULL;
      81             :     }
      82             : 
      83           0 :     if (match->type == XML_DOCUMENT_NODE) {
      84             :         /* Will happen if section = '/' */
      85           0 :         match = match->children;
      86             : 
      87           0 :     } else if (match->type != XML_ELEMENT_NODE
      88           0 :                && match->parent && match->parent->type == XML_ELEMENT_NODE) {
      89             :         /* Return the parent instead */
      90           0 :         match = match->parent;
      91             : 
      92           0 :     } else if (match->type != XML_ELEMENT_NODE) {
      93             :         /* We only support searching nodes */
      94           0 :         crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type);
      95           0 :         match = NULL;
      96             :     }
      97           0 :     return match;
      98             : }
      99             : 
     100             : void
     101           0 : dedupXpathResults(xmlXPathObjectPtr xpathObj)
     102             : {
     103           0 :     int lpc, max = numXpathResults(xpathObj);
     104             : 
     105           0 :     if (xpathObj == NULL) {
     106           0 :         return;
     107             :     }
     108             : 
     109           0 :     for (lpc = 0; lpc < max; lpc++) {
     110           0 :         xmlNode *xml = NULL;
     111           0 :         gboolean dedup = FALSE;
     112             : 
     113           0 :         if (xpathObj->nodesetval->nodeTab[lpc] == NULL) {
     114           0 :             continue;
     115             :         }
     116             : 
     117           0 :         xml = xpathObj->nodesetval->nodeTab[lpc]->parent;
     118             : 
     119           0 :         for (; xml; xml = xml->parent) {
     120           0 :             int lpc2 = 0;
     121             : 
     122           0 :             for (lpc2 = 0; lpc2 < max; lpc2++) {
     123           0 :                 if (xpathObj->nodesetval->nodeTab[lpc2] == xml) {
     124           0 :                     xpathObj->nodesetval->nodeTab[lpc] = NULL;
     125           0 :                     dedup = TRUE;
     126           0 :                     break;
     127             :                 }
     128             :             }
     129             : 
     130           0 :             if (dedup) {
     131           0 :                 break;
     132             :             }
     133             :         }
     134             :     }
     135             : }
     136             : 
     137             : /* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
     138             : xmlXPathObjectPtr
     139           0 : xpath_search(const xmlNode *xml_top, const char *path)
     140             : {
     141           0 :     xmlXPathObjectPtr xpathObj = NULL;
     142           0 :     xmlXPathContextPtr xpathCtx = NULL;
     143           0 :     const xmlChar *xpathExpr = (pcmkXmlStr) path;
     144             : 
     145           0 :     CRM_CHECK(path != NULL, return NULL);
     146           0 :     CRM_CHECK(xml_top != NULL, return NULL);
     147           0 :     CRM_CHECK(strlen(path) > 0, return NULL);
     148             : 
     149           0 :     xpathCtx = xmlXPathNewContext(xml_top->doc);
     150           0 :     pcmk__mem_assert(xpathCtx);
     151             : 
     152           0 :     xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
     153           0 :     xmlXPathFreeContext(xpathCtx);
     154           0 :     return xpathObj;
     155             : }
     156             : 
     157             : /*!
     158             :  * \brief Run a supplied function for each result of an xpath search
     159             :  *
     160             :  * \param[in,out] xml        XML to search
     161             :  * \param[in]     xpath      XPath search string
     162             :  * \param[in]     helper     Function to call for each result
     163             :  * \param[in,out] user_data  Data to pass to supplied function
     164             :  *
     165             :  * \note The helper function will be passed the XML node of the result,
     166             :  *       and the supplied user_data. This function does not otherwise
     167             :  *       use user_data.
     168             :  */
     169             : void
     170           0 : crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
     171             :                          void (*helper)(xmlNode*, void*), void *user_data)
     172             : {
     173           0 :     xmlXPathObjectPtr xpathObj = xpath_search(xml, xpath);
     174           0 :     int nresults = numXpathResults(xpathObj);
     175             :     int i;
     176             : 
     177           0 :     for (i = 0; i < nresults; i++) {
     178           0 :         xmlNode *result = getXpathResult(xpathObj, i);
     179             : 
     180           0 :         CRM_LOG_ASSERT(result != NULL);
     181           0 :         if (result) {
     182           0 :             (*helper)(result, user_data);
     183             :         }
     184             :     }
     185           0 :     freeXpathObject(xpathObj);
     186           0 : }
     187             : 
     188             : xmlNode *
     189           0 : get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level)
     190             : {
     191             :     int max;
     192           0 :     xmlNode *result = NULL;
     193           0 :     xmlXPathObjectPtr xpathObj = NULL;
     194           0 :     char *nodePath = NULL;
     195           0 :     char *matchNodePath = NULL;
     196             : 
     197           0 :     if (xpath == NULL) {
     198           0 :         return xml_obj;         /* or return NULL? */
     199             :     }
     200             : 
     201           0 :     xpathObj = xpath_search(xml_obj, xpath);
     202           0 :     nodePath = (char *)xmlGetNodePath(xml_obj);
     203           0 :     max = numXpathResults(xpathObj);
     204             : 
     205           0 :     if (max < 1) {
     206           0 :         if (error_level < LOG_NEVER) {
     207           0 :             do_crm_log(error_level, "No match for %s in %s",
     208             :                        xpath, pcmk__s(nodePath, "unknown path"));
     209           0 :             crm_log_xml_explicit(xml_obj, "Unexpected Input");
     210             :         }
     211             : 
     212           0 :     } else if (max > 1) {
     213           0 :         if (error_level < LOG_NEVER) {
     214           0 :             int lpc = 0;
     215             : 
     216           0 :             do_crm_log(error_level, "Too many matches for %s in %s",
     217             :                        xpath, pcmk__s(nodePath, "unknown path"));
     218             : 
     219           0 :             for (lpc = 0; lpc < max; lpc++) {
     220           0 :                 xmlNode *match = getXpathResult(xpathObj, lpc);
     221             : 
     222           0 :                 CRM_LOG_ASSERT(match != NULL);
     223           0 :                 if (match != NULL) {
     224           0 :                     matchNodePath = (char *) xmlGetNodePath(match);
     225           0 :                     do_crm_log(error_level, "%s[%d] = %s",
     226             :                                xpath, lpc,
     227             :                                pcmk__s(matchNodePath, "unrecognizable match"));
     228           0 :                     free(matchNodePath);
     229             :                 }
     230             :             }
     231           0 :             crm_log_xml_explicit(xml_obj, "Bad Input");
     232             :         }
     233             : 
     234             :     } else {
     235           0 :         result = getXpathResult(xpathObj, 0);
     236             :     }
     237             : 
     238           0 :     freeXpathObject(xpathObj);
     239           0 :     free(nodePath);
     240             : 
     241           0 :     return result;
     242             : }
     243             : 
     244             : /*!
     245             :  * \internal
     246             :  * \brief Get an XPath string that matches an XML element as closely as possible
     247             :  *
     248             :  * \param[in] xml  The XML element for which to build an XPath string
     249             :  *
     250             :  * \return A \p GString that matches \p xml, or \p NULL if \p xml is \p NULL.
     251             :  *
     252             :  * \note The caller is responsible for freeing the string using
     253             :  *       \p g_string_free().
     254             :  */
     255             : GString *
     256           0 : pcmk__element_xpath(const xmlNode *xml)
     257             : {
     258           0 :     const xmlNode *parent = NULL;
     259           0 :     GString *xpath = NULL;
     260           0 :     const char *id = NULL;
     261             : 
     262           0 :     if (xml == NULL) {
     263           0 :         return NULL;
     264             :     }
     265             : 
     266           0 :     parent = xml->parent;
     267           0 :     xpath = pcmk__element_xpath(parent);
     268           0 :     if (xpath == NULL) {
     269           0 :         xpath = g_string_sized_new(256);
     270             :     }
     271             : 
     272             :     // Build xpath like "/" -> "/cib" -> "/cib/configuration"
     273           0 :     if (parent == NULL) {
     274             :         g_string_append_c(xpath, '/');
     275           0 :     } else if (parent->parent == NULL) {
     276           0 :         g_string_append(xpath, (const gchar *) xml->name);
     277             :     } else {
     278           0 :         pcmk__g_strcat(xpath, "/", (const char *) xml->name, NULL);
     279             :     }
     280             : 
     281           0 :     id = pcmk__xe_id(xml);
     282           0 :     if (id != NULL) {
     283           0 :         pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", id, "']", NULL);
     284             :     }
     285             : 
     286           0 :     return xpath;
     287             : }
     288             : 
     289             : char *
     290          15 : pcmk__xpath_node_id(const char *xpath, const char *node)
     291             : {
     292          15 :     char *retval = NULL;
     293          15 :     char *patt = NULL;
     294          15 :     char *start = NULL;
     295          15 :     char *end = NULL;
     296             : 
     297          15 :     if (node == NULL || xpath == NULL) {
     298           6 :         return retval;
     299             :     }
     300             : 
     301           9 :     patt = crm_strdup_printf("/%s[@" PCMK_XA_ID "=", node);
     302           9 :     start = strstr(xpath, patt);
     303             : 
     304           9 :     if (!start) {
     305           6 :         free(patt);
     306           6 :         return retval;
     307             :     }
     308             : 
     309           3 :     start += strlen(patt);
     310           3 :     start++;
     311             : 
     312           3 :     end = strstr(start, "\'");
     313           3 :     CRM_ASSERT(end);
     314           2 :     retval = strndup(start, end-start);
     315             : 
     316           2 :     free(patt);
     317           2 :     return retval;
     318             : }
     319             : 
     320             : static int
     321           0 : output_attr_child(xmlNode *child, void *userdata)
     322             : {
     323           0 :     pcmk__output_t *out = userdata;
     324             : 
     325           0 :     out->info(out, "  Value: %s \t(id=%s)",
     326             :               crm_element_value(child, PCMK_XA_VALUE),
     327             :               pcmk__s(pcmk__xe_id(child), "<none>"));
     328           0 :     return pcmk_rc_ok;
     329             : }
     330             : 
     331             : void
     332           0 : pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search,
     333             :                                  const char *name)
     334             : {
     335           0 :     if (out == NULL || name == NULL || search == NULL ||
     336           0 :         search->children == NULL) {
     337           0 :         return;
     338             :     }
     339             : 
     340           0 :     out->info(out, "Multiple attributes match " PCMK_XA_NAME "=%s", name);
     341           0 :     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             : 
     349             : /*!
     350             :  * \deprecated This function will be removed in a future release
     351             :  * \brief Get an XPath string that matches an XML element as closely as possible
     352             :  *
     353             :  * \param[in] xml  The XML element for which to build an XPath string
     354             :  *
     355             :  * \return A string that matches \p xml, or \p NULL if \p xml is \p NULL.
     356             :  *
     357             :  * \note The caller is responsible for freeing the string using free().
     358             :  */
     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

Generated by: LCOV version 1.14