LCOV - code coverage report
Current view: top level - common - nvpair.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 46 286 16.1 %
Date: 2024-05-07 11:09:47 Functions: 6 35 17.1 %

          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             : 
      12             : #include <stdio.h>
      13             : #include <sys/types.h>
      14             : #include <string.h>
      15             : #include <ctype.h>
      16             : #include <glib.h>
      17             : #include <libxml/tree.h>
      18             : 
      19             : #include <crm/crm.h>
      20             : #include <crm/common/xml.h>
      21             : #include <crm/common/xml_internal.h>
      22             : #include "crmcommon_private.h"
      23             : 
      24             : /*
      25             :  * This file isolates handling of various kinds of name/value pairs:
      26             :  *
      27             :  * - pcmk_nvpair_t data type
      28             :  * - XML attributes (<TAG ... NAME=VALUE ...>)
      29             :  * - XML nvpair elements (<nvpair id=ID name=NAME value=VALUE>)
      30             :  * - Meta-attributes (for resources and actions)
      31             :  */
      32             : 
      33             : // pcmk_nvpair_t handling
      34             : 
      35             : /*!
      36             :  * \internal
      37             :  * \brief Allocate a new name/value pair
      38             :  *
      39             :  * \param[in] name   New name (required)
      40             :  * \param[in] value  New value
      41             :  *
      42             :  * \return Newly allocated name/value pair
      43             :  * \note The caller is responsible for freeing the result with
      44             :  *       \c pcmk__free_nvpair().
      45             :  */
      46             : static pcmk_nvpair_t *
      47           0 : pcmk__new_nvpair(const char *name, const char *value)
      48             : {
      49           0 :     pcmk_nvpair_t *nvpair = NULL;
      50             : 
      51           0 :     CRM_ASSERT(name);
      52             : 
      53           0 :     nvpair = pcmk__assert_alloc(1, sizeof(pcmk_nvpair_t));
      54             : 
      55           0 :     nvpair->name = pcmk__str_copy(name);
      56           0 :     nvpair->value = pcmk__str_copy(value);
      57           0 :     return nvpair;
      58             : }
      59             : 
      60             : /*!
      61             :  * \internal
      62             :  * \brief Free a name/value pair
      63             :  *
      64             :  * \param[in,out] nvpair  Name/value pair to free
      65             :  */
      66             : static void
      67           0 : pcmk__free_nvpair(gpointer data)
      68             : {
      69           0 :     if (data) {
      70           0 :         pcmk_nvpair_t *nvpair = data;
      71             : 
      72           0 :         free(nvpair->name);
      73           0 :         free(nvpair->value);
      74           0 :         free(nvpair);
      75             :     }
      76           0 : }
      77             : 
      78             : /*!
      79             :  * \brief Prepend a name/value pair to a list
      80             :  *
      81             :  * \param[in,out] nvpairs  List to modify
      82             :  * \param[in]     name     New entry's name
      83             :  * \param[in]     value    New entry's value
      84             :  *
      85             :  * \return New head of list
      86             :  * \note The caller is responsible for freeing the list with
      87             :  *       \c pcmk_free_nvpairs().
      88             :  */
      89             : GSList *
      90           0 : pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
      91             : {
      92           0 :     return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
      93             : }
      94             : 
      95             : /*!
      96             :  * \brief Free a list of name/value pairs
      97             :  *
      98             :  * \param[in,out] list  List to free
      99             :  */
     100             : void
     101           0 : pcmk_free_nvpairs(GSList *nvpairs)
     102             : {
     103           0 :     g_slist_free_full(nvpairs, pcmk__free_nvpair);
     104           0 : }
     105             : 
     106             : /*!
     107             :  * \internal
     108             :  * \brief Compare two name/value pairs
     109             :  *
     110             :  * \param[in] a  First name/value pair to compare
     111             :  * \param[in] b  Second name/value pair to compare
     112             :  *
     113             :  * \return 0 if a == b, 1 if a > b, -1 if a < b
     114             :  */
     115             : static gint
     116           0 : pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
     117             : {
     118           0 :     int rc = 0;
     119           0 :     const pcmk_nvpair_t *pair_a = a;
     120           0 :     const pcmk_nvpair_t *pair_b = b;
     121             : 
     122           0 :     CRM_ASSERT(a != NULL);
     123           0 :     CRM_ASSERT(pair_a->name != NULL);
     124             : 
     125           0 :     CRM_ASSERT(b != NULL);
     126           0 :     CRM_ASSERT(pair_b->name != NULL);
     127             : 
     128           0 :     rc = strcmp(pair_a->name, pair_b->name);
     129           0 :     if (rc < 0) {
     130           0 :         return -1;
     131           0 :     } else if (rc > 0) {
     132           0 :         return 1;
     133             :     }
     134           0 :     return 0;
     135             : }
     136             : 
     137             : /*!
     138             :  * \brief Sort a list of name/value pairs
     139             :  *
     140             :  * \param[in,out] list  List to sort
     141             :  *
     142             :  * \return New head of list
     143             :  */
     144             : GSList *
     145           0 : pcmk_sort_nvpairs(GSList *list)
     146             : {
     147           0 :     return g_slist_sort(list, pcmk__compare_nvpair);
     148             : }
     149             : 
     150             : /*!
     151             :  * \brief Create a list of name/value pairs from an XML node's attributes
     152             :  *
     153             :  * \param[in]  XML to parse
     154             :  *
     155             :  * \return New list of name/value pairs
     156             :  * \note It is the caller's responsibility to free the list with
     157             :  *       \c pcmk_free_nvpairs().
     158             :  */
     159             : GSList *
     160           0 : pcmk_xml_attrs2nvpairs(const xmlNode *xml)
     161             : {
     162           0 :     GSList *result = NULL;
     163             : 
     164           0 :     for (xmlAttrPtr iter = pcmk__xe_first_attr(xml); iter != NULL;
     165           0 :          iter = iter->next) {
     166             : 
     167           0 :         result = pcmk_prepend_nvpair(result,
     168           0 :                                      (const char *) iter->name,
     169             :                                      (const char *) pcmk__xml_attr_value(iter));
     170             :     }
     171           0 :     return result;
     172             : }
     173             : 
     174             : /*!
     175             :  * \internal
     176             :  * \brief Add an XML attribute corresponding to a name/value pair
     177             :  *
     178             :  * Suitable for glib list iterators, this function adds a NAME=VALUE
     179             :  * XML attribute based on a given name/value pair.
     180             :  *
     181             :  * \param[in]  data       Name/value pair
     182             :  * \param[out] user_data  XML node to add attributes to
     183             :  */
     184             : static void
     185           0 : pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
     186             : {
     187           0 :     pcmk_nvpair_t *pair = data;
     188           0 :     xmlNode *parent = user_data;
     189             : 
     190           0 :     crm_xml_add(parent, pair->name, pair->value);
     191           0 : }
     192             : 
     193             : /*!
     194             :  * \brief Add XML attributes based on a list of name/value pairs
     195             :  *
     196             :  * \param[in,out] list  List of name/value pairs
     197             :  * \param[in,out] xml   XML node to add attributes to
     198             :  */
     199             : void
     200           0 : pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
     201             : {
     202           0 :     g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
     203           0 : }
     204             : 
     205             : // convenience function for name=value strings
     206             : 
     207             : /*!
     208             :  * \internal
     209             :  * \brief Extract the name and value from an input string formatted as "name=value".
     210             :  * If unable to extract them, they are returned as NULL.
     211             :  *
     212             :  * \param[in]  input The input string, likely from the command line
     213             :  * \param[out] name  Everything before the first '=' in the input string
     214             :  * \param[out] value Everything after the first '=' in the input string
     215             :  *
     216             :  * \return 2 if both name and value could be extracted, 1 if only one could, and
     217             :  *         and error code otherwise
     218             :  */
     219             : int
     220           0 : pcmk__scan_nvpair(const char *input, char **name, char **value)
     221             : {
     222             : #ifdef HAVE_SSCANF_M
     223           0 :     *name = NULL;
     224           0 :     *value = NULL;
     225           0 :     if (sscanf(input, "%m[^=]=%m[^\n]", name, value) <= 0) {
     226           0 :         return -pcmk_err_bad_nvpair;
     227             :     }
     228             : #else
     229             :     char *sep = NULL;
     230             :     *name = NULL;
     231             :     *value = NULL;
     232             : 
     233             :     sep = strstr(optarg, "=");
     234             :     if (sep == NULL) {
     235             :         return -pcmk_err_bad_nvpair;
     236             :     }
     237             : 
     238             :     *name = strndup(input, sep-input);
     239             : 
     240             :     if (*name == NULL) {
     241             :         return -ENOMEM;
     242             :     }
     243             : 
     244             :     /* If the last char in optarg is =, the user gave no
     245             :      * value for the option.  Leave it as NULL.
     246             :      */
     247             :     if (*(sep+1) != '\0') {
     248             :         *value = strdup(sep+1);
     249             : 
     250             :         if (*value == NULL) {
     251             :             return -ENOMEM;
     252             :         }
     253             :     }
     254             : #endif
     255             : 
     256           0 :     if (*name != NULL && *value != NULL) {
     257           0 :         return 2;
     258           0 :     } else if (*name != NULL || *value != NULL) {
     259           0 :         return 1;
     260             :     } else {
     261           0 :         return -pcmk_err_bad_nvpair;
     262             :     }
     263             : }
     264             : 
     265             : /*!
     266             :  * \internal
     267             :  * \brief Format a name/value pair.
     268             :  *
     269             :  * Units can optionally be provided for the value.  Note that unlike most
     270             :  * formatting functions, this one returns the formatted string.  It is
     271             :  * assumed that the most common use of this function will be to build up
     272             :  * a string to be output as part of other functions.
     273             :  *
     274             :  * \note The caller is responsible for freeing the return value after use.
     275             :  *
     276             :  * \param[in]     name  The name of the nvpair.
     277             :  * \param[in]     value The value of the nvpair.
     278             :  * \param[in]     units Optional units for the value, or NULL.
     279             :  *
     280             :  * \return Newly allocated string with name/value pair
     281             :  */
     282             : char *
     283           0 : pcmk__format_nvpair(const char *name, const char *value, const char *units)
     284             : {
     285           0 :     return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
     286             : }
     287             : 
     288             : // XML attribute handling
     289             : 
     290             : /*!
     291             :  * \brief Create an XML attribute with specified name and value
     292             :  *
     293             :  * \param[in,out] node   XML node to modify
     294             :  * \param[in]     name   Attribute name to set
     295             :  * \param[in]     value  Attribute value to set
     296             :  *
     297             :  * \return New value on success, \c NULL otherwise
     298             :  * \note This does nothing if node, name, or value are \c NULL or empty.
     299             :  */
     300             : const char *
     301           0 : crm_xml_add(xmlNode *node, const char *name, const char *value)
     302             : {
     303           0 :     bool dirty = FALSE;
     304           0 :     xmlAttr *attr = NULL;
     305             : 
     306           0 :     CRM_CHECK(node != NULL, return NULL);
     307           0 :     CRM_CHECK(name != NULL, return NULL);
     308             : 
     309           0 :     if (value == NULL) {
     310           0 :         return NULL;
     311             :     }
     312             : 
     313           0 :     if (pcmk__tracking_xml_changes(node, FALSE)) {
     314           0 :         const char *old = crm_element_value(node, name);
     315             : 
     316           0 :         if (old == NULL || value == NULL || strcmp(old, value) != 0) {
     317           0 :             dirty = TRUE;
     318             :         }
     319             :     }
     320             : 
     321           0 :     if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
     322           0 :         crm_trace("Cannot add %s=%s to %s", name, value, node->name);
     323           0 :         return NULL;
     324             :     }
     325             : 
     326           0 :     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
     327           0 :     if (dirty) {
     328           0 :         pcmk__mark_xml_attr_dirty(attr);
     329             :     }
     330             : 
     331           0 :     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
     332           0 :     return (char *)attr->children->content;
     333             : }
     334             : 
     335             : /*!
     336             :  * \brief Create an XML attribute with specified name and integer value
     337             :  *
     338             :  * This is like \c crm_xml_add() but taking an integer value.
     339             :  *
     340             :  * \param[in,out] node   XML node to modify
     341             :  * \param[in]     name   Attribute name to set
     342             :  * \param[in]     value  Attribute value to set
     343             :  *
     344             :  * \return New value as string on success, \c NULL otherwise
     345             :  * \note This does nothing if node or name are \c NULL or empty.
     346             :  */
     347             : const char *
     348           0 : crm_xml_add_int(xmlNode *node, const char *name, int value)
     349             : {
     350           0 :     char *number = pcmk__itoa(value);
     351           0 :     const char *added = crm_xml_add(node, name, number);
     352             : 
     353           0 :     free(number);
     354           0 :     return added;
     355             : }
     356             : 
     357             : /*!
     358             :  * \brief Create an XML attribute with specified name and unsigned value
     359             :  *
     360             :  * This is like \c crm_xml_add() but taking a guint value.
     361             :  *
     362             :  * \param[in,out] node   XML node to modify
     363             :  * \param[in]     name   Attribute name to set
     364             :  * \param[in]     ms     Attribute value to set
     365             :  *
     366             :  * \return New value as string on success, \c NULL otherwise
     367             :  * \note This does nothing if node or name are \c NULL or empty.
     368             :  */
     369             : const char *
     370           0 : crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
     371             : {
     372           0 :     char *number = crm_strdup_printf("%u", ms);
     373           0 :     const char *added = crm_xml_add(node, name, number);
     374             : 
     375           0 :     free(number);
     376           0 :     return added;
     377             : }
     378             : 
     379             : // Maximum size of null-terminated string representation of 64-bit integer
     380             : // -9223372036854775808
     381             : #define LLSTRSIZE 21
     382             : 
     383             : /*!
     384             :  * \brief Create an XML attribute with specified name and long long int value
     385             :  *
     386             :  * This is like \c crm_xml_add() but taking a long long int value. It is a
     387             :  * useful equivalent for defined types like time_t, etc.
     388             :  *
     389             :  * \param[in,out] xml    XML node to modify
     390             :  * \param[in]     name   Attribute name to set
     391             :  * \param[in]     value  Attribute value to set
     392             :  *
     393             :  * \return New value as string on success, \c NULL otherwise
     394             :  * \note This does nothing if xml or name are \c NULL or empty.
     395             :  *       This does not support greater than 64-bit values.
     396             :  */
     397             : const char *
     398           0 : crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
     399             : {
     400           0 :     char s[LLSTRSIZE] = { '\0', };
     401             : 
     402           0 :     if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
     403           0 :         return NULL;
     404             :     }
     405           0 :     return crm_xml_add(xml, name, s);
     406             : }
     407             : 
     408             : /*!
     409             :  * \brief Create XML attributes for seconds and microseconds
     410             :  *
     411             :  * This is like \c crm_xml_add() but taking a struct timeval.
     412             :  *
     413             :  * \param[in,out] xml        XML node to modify
     414             :  * \param[in]     name_sec   Name of XML attribute for seconds
     415             :  * \param[in]     name_usec  Name of XML attribute for microseconds (or NULL)
     416             :  * \param[in]     value      Time value to set
     417             :  *
     418             :  * \return New seconds value as string on success, \c NULL otherwise
     419             :  * \note This does nothing if xml, name_sec, or value is \c NULL.
     420             :  */
     421             : const char *
     422           0 : crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
     423             :                     const struct timeval *value)
     424             : {
     425           0 :     const char *added = NULL;
     426             : 
     427           0 :     if (xml && name_sec && value) {
     428           0 :         added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
     429           0 :         if (added && name_usec) {
     430             :             // Any error is ignored (we successfully added seconds)
     431           0 :             crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
     432             :         }
     433             :     }
     434           0 :     return added;
     435             : }
     436             : 
     437             : /*!
     438             :  * \brief Retrieve the value of an XML attribute
     439             :  *
     440             :  * \param[in] data   XML node to check
     441             :  * \param[in] name   Attribute name to check
     442             :  *
     443             :  * \return Value of specified attribute (may be \c NULL)
     444             :  */
     445             : const char *
     446           0 : crm_element_value(const xmlNode *data, const char *name)
     447             : {
     448           0 :     xmlAttr *attr = NULL;
     449             : 
     450           0 :     if (data == NULL) {
     451           0 :         crm_err("Couldn't find %s in NULL", name ? name : "<null>");
     452           0 :         CRM_LOG_ASSERT(data != NULL);
     453           0 :         return NULL;
     454             : 
     455           0 :     } else if (name == NULL) {
     456           0 :         crm_err("Couldn't find NULL in %s", data->name);
     457           0 :         return NULL;
     458             :     }
     459             : 
     460             :     /* The first argument to xmlHasProp() has always been const,
     461             :      * but libxml2 <2.9.2 didn't declare that, so cast it
     462             :      */
     463           0 :     attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
     464           0 :     if (!attr || !attr->children) {
     465           0 :         return NULL;
     466             :     }
     467           0 :     return (const char *) attr->children->content;
     468             : }
     469             : 
     470             : /*!
     471             :  * \brief Retrieve the integer value of an XML attribute
     472             :  *
     473             :  * This is like \c crm_element_value() but getting the value as an integer.
     474             :  *
     475             :  * \param[in]  data  XML node to check
     476             :  * \param[in]  name  Attribute name to check
     477             :  * \param[out] dest  Where to store element value
     478             :  *
     479             :  * \return 0 on success, -1 otherwise
     480             :  */
     481             : int
     482           0 : crm_element_value_int(const xmlNode *data, const char *name, int *dest)
     483             : {
     484           0 :     const char *value = NULL;
     485             : 
     486           0 :     CRM_CHECK(dest != NULL, return -1);
     487           0 :     value = crm_element_value(data, name);
     488           0 :     if (value) {
     489             :         long long value_ll;
     490             : 
     491           0 :         if ((pcmk__scan_ll(value, &value_ll, 0LL) != pcmk_rc_ok)
     492           0 :             || (value_ll < INT_MIN) || (value_ll > INT_MAX)) {
     493           0 :             *dest = PCMK__PARSE_INT_DEFAULT;
     494             :         } else {
     495           0 :             *dest = (int) value_ll;
     496           0 :             return 0;
     497             :         }
     498             :     }
     499           0 :     return -1;
     500             : }
     501             : 
     502             : /*!
     503             :  * \brief Retrieve the long long integer value of an XML attribute
     504             :  *
     505             :  * This is like \c crm_element_value() but getting the value as a long long int.
     506             :  *
     507             :  * \param[in]  data  XML node to check
     508             :  * \param[in]  name  Attribute name to check
     509             :  * \param[out] dest  Where to store element value
     510             :  *
     511             :  * \return 0 on success, -1 otherwise
     512             :  */
     513             : int
     514           0 : crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
     515             : {
     516           0 :     const char *value = NULL;
     517             : 
     518           0 :     CRM_CHECK(dest != NULL, return -1);
     519           0 :     value = crm_element_value(data, name);
     520           0 :     if ((value != NULL)
     521           0 :         && (pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT) == pcmk_rc_ok)) {
     522           0 :         return 0;
     523             :     }
     524           0 :     return -1;
     525             : }
     526             : 
     527             : /*!
     528             :  * \brief Retrieve the millisecond value of an XML attribute
     529             :  *
     530             :  * This is like \c crm_element_value() but returning the value as a guint.
     531             :  *
     532             :  * \param[in]  data   XML node to check
     533             :  * \param[in]  name   Attribute name to check
     534             :  * \param[out] dest   Where to store attribute value
     535             :  *
     536             :  * \return \c pcmk_ok on success, -1 otherwise
     537             :  */
     538             : int
     539           0 : crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
     540             : {
     541           0 :     const char *value = NULL;
     542             :     long long value_ll;
     543             : 
     544           0 :     CRM_CHECK(dest != NULL, return -1);
     545           0 :     *dest = 0;
     546           0 :     value = crm_element_value(data, name);
     547           0 :     if ((pcmk__scan_ll(value, &value_ll, 0LL) != pcmk_rc_ok)
     548           0 :         || (value_ll < 0) || (value_ll > G_MAXUINT)) {
     549           0 :         return -1;
     550             :     }
     551           0 :     *dest = (guint) value_ll;
     552           0 :     return pcmk_ok;
     553             : }
     554             : 
     555             : /*!
     556             :  * \brief Retrieve the seconds-since-epoch value of an XML attribute
     557             :  *
     558             :  * This is like \c crm_element_value() but returning the value as a time_t.
     559             :  *
     560             :  * \param[in]  xml    XML node to check
     561             :  * \param[in]  name   Attribute name to check
     562             :  * \param[out] dest   Where to store attribute value
     563             :  *
     564             :  * \return \c pcmk_ok on success, -1 otherwise
     565             :  */
     566             : int
     567           0 : crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
     568             : {
     569           0 :     long long value_ll = 0;
     570             : 
     571           0 :     if (crm_element_value_ll(xml, name, &value_ll) < 0) {
     572           0 :         return -1;
     573             :     }
     574             : 
     575             :     /* Unfortunately, we can't do any bounds checking, since time_t has neither
     576             :      * standardized bounds nor constants defined for them.
     577             :      */
     578           0 :     *dest = (time_t) value_ll;
     579           0 :     return pcmk_ok;
     580             : }
     581             : 
     582             : /*!
     583             :  * \brief Retrieve the value of XML second/microsecond attributes as time
     584             :  *
     585             :  * This is like \c crm_element_value() but returning value as a struct timeval.
     586             :  *
     587             :  * \param[in]  xml        XML to parse
     588             :  * \param[in]  name_sec   Name of XML attribute for seconds
     589             :  * \param[in]  name_usec  Name of XML attribute for microseconds
     590             :  * \param[out] dest       Where to store result
     591             :  *
     592             :  * \return \c pcmk_ok on success, -errno on error
     593             :  * \note Values default to 0 if XML or XML attribute does not exist
     594             :  */
     595             : int
     596           0 : crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
     597             :                           const char *name_usec, struct timeval *dest)
     598             : {
     599           0 :     long long value_i = 0;
     600             : 
     601           0 :     CRM_CHECK(dest != NULL, return -EINVAL);
     602           0 :     dest->tv_sec = 0;
     603           0 :     dest->tv_usec = 0;
     604             : 
     605           0 :     if (xml == NULL) {
     606           0 :         return pcmk_ok;
     607             :     }
     608             : 
     609             :     /* Unfortunately, we can't do any bounds checking, since there are no
     610             :      * constants provided for the bounds of time_t and suseconds_t, and
     611             :      * calculating them isn't worth the effort. If there are XML values
     612             :      * beyond the native sizes, there will probably be worse problems anyway.
     613             :      */
     614             : 
     615             :     // Parse seconds
     616           0 :     errno = 0;
     617           0 :     if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
     618           0 :         return -errno;
     619             :     }
     620           0 :     dest->tv_sec = (time_t) value_i;
     621             : 
     622             :     // Parse microseconds
     623           0 :     if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
     624           0 :         return -errno;
     625             :     }
     626           0 :     dest->tv_usec = (suseconds_t) value_i;
     627             : 
     628           0 :     return pcmk_ok;
     629             : }
     630             : 
     631             : /*!
     632             :  * \internal
     633             :  * \brief Get a date/time object from an XML attribute value
     634             :  *
     635             :  * \param[in]  xml   XML with attribute to parse (from CIB)
     636             :  * \param[in]  attr  Name of attribute to parse
     637             :  * \param[out] t     Where to create date/time object
     638             :  *                   (\p *t must be NULL initially)
     639             :  *
     640             :  * \return Standard Pacemaker return code
     641             :  * \note The caller is responsible for freeing \p *t using crm_time_free().
     642             :  */
     643             : int
     644         112 : pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
     645             : {
     646         112 :     const char *value = NULL;
     647             : 
     648         112 :     if ((t == NULL) || (*t != NULL) || (xml == NULL) || (attr == NULL)) {
     649           8 :         return EINVAL;
     650             :     }
     651             : 
     652         104 :     value = crm_element_value(xml, attr);
     653         104 :     if (value != NULL) {
     654          73 :         *t = crm_time_new(value);
     655          73 :         if (*t == NULL) {
     656          15 :             return pcmk_rc_unpack_error;
     657             :         }
     658             :     }
     659          89 :     return pcmk_rc_ok;
     660             : }
     661             : 
     662             : /*!
     663             :  * \brief Retrieve a copy of the value of an XML attribute
     664             :  *
     665             :  * This is like \c crm_element_value() but allocating new memory for the result.
     666             :  *
     667             :  * \param[in] data   XML node to check
     668             :  * \param[in] name   Attribute name to check
     669             :  *
     670             :  * \return Value of specified attribute (may be \c NULL)
     671             :  * \note The caller is responsible for freeing the result.
     672             :  */
     673             : char *
     674           0 : crm_element_value_copy(const xmlNode *data, const char *name)
     675             : {
     676           0 :     return pcmk__str_copy(crm_element_value(data, name));
     677             : }
     678             : 
     679             : /*!
     680             :  * \brief Safely add hash table entry to XML as attribute or name-value pair
     681             :  *
     682             :  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
     683             :  * and value, with an XML node passed as user data, and adds an XML attribute
     684             :  * with the specified name and value if it does not already exist. If the key
     685             :  * name starts with a digit, then it's not a valid XML attribute name. In that
     686             :  * case, this will instead add a <tt><param name=NAME value=VALUE/></tt> child
     687             :  * to the XML.
     688             :  *
     689             :  * \param[in]     key        Key of hash table entry
     690             :  * \param[in]     value      Value of hash table entry
     691             :  * \param[in,out] user_data  XML node
     692             :  */
     693             : void
     694           0 : hash2smartfield(gpointer key, gpointer value, gpointer user_data)
     695             : {
     696             :     /* @TODO Generate PCMK__XE_PARAM nodes for all keys that aren't valid XML
     697             :      * attribute names (not just those that start with digits), or possibly for
     698             :      * all keys to simplify parsing.
     699             :      *
     700             :      * Consider either deprecating as public API or exposing PCMK__XE_PARAM.
     701             :      * PCMK__XE_PARAM is currently private because it doesn't appear in any
     702             :      * output that Pacemaker generates.
     703             :      */
     704           0 :     const char *name = key;
     705           0 :     const char *s_value = value;
     706             : 
     707           0 :     xmlNode *xml_node = user_data;
     708             : 
     709           0 :     if (isdigit(name[0])) {
     710           0 :         xmlNode *tmp = pcmk__xe_create(xml_node, PCMK__XE_PARAM);
     711             : 
     712           0 :         crm_xml_add(tmp, PCMK_XA_NAME, name);
     713           0 :         crm_xml_add(tmp, PCMK_XA_VALUE, s_value);
     714             : 
     715           0 :     } else if (crm_element_value(xml_node, name) == NULL) {
     716           0 :         crm_xml_add(xml_node, name, s_value);
     717           0 :         crm_trace("dumped: %s=%s", name, s_value);
     718             : 
     719             :     } else {
     720           0 :         crm_trace("duplicate: %s=%s", name, s_value);
     721             :     }
     722           0 : }
     723             : 
     724             : /*!
     725             :  * \brief Set XML attribute based on hash table entry
     726             :  *
     727             :  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
     728             :  * and value, with an XML node passed as user data, and adds an XML attribute
     729             :  * with the specified name and value if it does not already exist.
     730             :  *
     731             :  * \param[in]     key        Key of hash table entry
     732             :  * \param[in]     value      Value of hash table entry
     733             :  * \param[in,out] user_data  XML node
     734             :  */
     735             : void
     736           0 : hash2field(gpointer key, gpointer value, gpointer user_data)
     737             : {
     738           0 :     const char *name = key;
     739           0 :     const char *s_value = value;
     740             : 
     741           0 :     xmlNode *xml_node = user_data;
     742             : 
     743           0 :     if (crm_element_value(xml_node, name) == NULL) {
     744           0 :         crm_xml_add(xml_node, name, s_value);
     745             : 
     746             :     } else {
     747           0 :         crm_trace("duplicate: %s=%s", name, s_value);
     748             :     }
     749           0 : }
     750             : 
     751             : /*!
     752             :  * \brief Set XML attribute based on hash table entry, as meta-attribute name
     753             :  *
     754             :  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
     755             :  * and value, with an XML node passed as user data, and adds an XML attribute
     756             :  * with the meta-attribute version of the specified name and value if it does
     757             :  * not already exist and if the name does not appear to be cluster-internal.
     758             :  *
     759             :  * \param[in]     key        Key of hash table entry
     760             :  * \param[in]     value      Value of hash table entry
     761             :  * \param[in,out] user_data  XML node
     762             :  */
     763             : void
     764           0 : hash2metafield(gpointer key, gpointer value, gpointer user_data)
     765             : {
     766           0 :     char *crm_name = NULL;
     767             : 
     768           0 :     if (key == NULL || value == NULL) {
     769           0 :         return;
     770             :     }
     771             : 
     772             :     /* Filter out cluster-generated attributes that contain a '#' or ':'
     773             :      * (like fail-count and last-failure).
     774             :      */
     775           0 :     for (crm_name = key; *crm_name; ++crm_name) {
     776           0 :         if ((*crm_name == '#') || (*crm_name == ':')) {
     777           0 :             return;
     778             :         }
     779             :     }
     780             : 
     781           0 :     crm_name = crm_meta_name(key);
     782           0 :     hash2field(crm_name, value, user_data);
     783           0 :     free(crm_name);
     784             : }
     785             : 
     786             : // nvpair handling
     787             : 
     788             : /*!
     789             :  * \brief Create an XML name/value pair
     790             :  *
     791             :  * \param[in,out] parent  If not \c NULL, make new XML node a child of this one
     792             :  * \param[in]     id      Set this as XML ID (or NULL to auto-generate)
     793             :  * \param[in]     name    Name to use
     794             :  * \param[in]     value   Value to use
     795             :  *
     796             :  * \return New XML object on success, \c NULL otherwise
     797             :  */
     798             : xmlNode *
     799           0 : crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
     800             :                       const char *value)
     801             : {
     802             :     xmlNode *nvp;
     803             : 
     804             :     /* id can be NULL so we auto-generate one, and name can be NULL if this
     805             :      * will be used to delete a name/value pair by ID, but both can't be NULL
     806             :      */
     807           0 :     CRM_CHECK(id || name, return NULL);
     808             : 
     809           0 :     nvp = pcmk__xe_create(parent, PCMK_XE_NVPAIR);
     810             : 
     811           0 :     if (id) {
     812           0 :         crm_xml_add(nvp, PCMK_XA_ID, id);
     813             :     } else {
     814           0 :         crm_xml_set_id(nvp, "%s-%s",
     815             :                        pcmk__s(pcmk__xe_id(parent), PCMK_XE_NVPAIR), name);
     816             :     }
     817           0 :     crm_xml_add(nvp, PCMK_XA_NAME, name);
     818           0 :     crm_xml_add(nvp, PCMK_XA_VALUE, value);
     819           0 :     return nvp;
     820             : }
     821             : 
     822             : /*!
     823             :  * \brief Add XML nvpair element based on hash table entry
     824             :  *
     825             :  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
     826             :  * and value, with an XML node passed as the user data, and adds an \c nvpair
     827             :  * XML element with the specified name and value.
     828             :  *
     829             :  * \param[in]     key        Key of hash table entry
     830             :  * \param[in]     value      Value of hash table entry
     831             :  * \param[in,out] user_data  XML node
     832             :  */
     833             : void
     834           0 : hash2nvpair(gpointer key, gpointer value, gpointer user_data)
     835             : {
     836           0 :     const char *name = key;
     837           0 :     const char *s_value = value;
     838           0 :     xmlNode *xml_node = user_data;
     839             : 
     840           0 :     crm_create_nvpair_xml(xml_node, name, name, s_value);
     841           0 :     crm_trace("dumped: name=%s value=%s", name, s_value);
     842           0 : }
     843             : 
     844             : /*!
     845             :  * \brief Retrieve XML attributes as a hash table
     846             :  *
     847             :  * Given an XML element, this will look for any \<attributes> element child,
     848             :  * creating a hash table of (newly allocated string) name/value pairs taken
     849             :  * first from the attributes element's NAME=VALUE XML attributes, and then
     850             :  * from any \<param name=NAME value=VALUE> children of attributes.
     851             :  *
     852             :  * \param[in]  XML node to parse
     853             :  *
     854             :  * \return Hash table with name/value pairs
     855             :  * \note It is the caller's responsibility to free the result using
     856             :  *       \c g_hash_table_destroy().
     857             :  */
     858             : GHashTable *
     859           0 : xml2list(const xmlNode *parent)
     860             : {
     861           0 :     xmlNode *child = NULL;
     862           0 :     xmlAttrPtr pIter = NULL;
     863           0 :     xmlNode *nvpair_list = NULL;
     864           0 :     GHashTable *nvpair_hash = pcmk__strkey_table(free, free);
     865             : 
     866           0 :     CRM_CHECK(parent != NULL, return nvpair_hash);
     867             : 
     868           0 :     nvpair_list = pcmk__xe_first_child(parent, PCMK__XE_ATTRIBUTES, NULL, NULL);
     869           0 :     if (nvpair_list == NULL) {
     870           0 :         crm_trace("No attributes in %s", parent->name);
     871           0 :         crm_log_xml_trace(parent, "No attributes for resource op");
     872             :     }
     873             : 
     874           0 :     crm_log_xml_trace(nvpair_list, "Unpacking");
     875             : 
     876           0 :     for (pIter = pcmk__xe_first_attr(nvpair_list); pIter != NULL;
     877           0 :          pIter = pIter->next) {
     878             : 
     879           0 :         const char *p_name = (const char *)pIter->name;
     880           0 :         const char *p_value = pcmk__xml_attr_value(pIter);
     881             : 
     882           0 :         crm_trace("Added %s=%s", p_name, p_value);
     883             : 
     884           0 :         pcmk__insert_dup(nvpair_hash, p_name, p_value);
     885             :     }
     886             : 
     887           0 :     for (child = pcmk__xe_first_child(nvpair_list, PCMK__XE_PARAM, NULL, NULL);
     888           0 :          child != NULL; child = pcmk__xe_next_same(child)) {
     889             : 
     890           0 :         const char *key = crm_element_value(child, PCMK_XA_NAME);
     891           0 :         const char *value = crm_element_value(child, PCMK_XA_VALUE);
     892             : 
     893           0 :         crm_trace("Added %s=%s", key, value);
     894           0 :         if (key != NULL && value != NULL) {
     895           0 :             pcmk__insert_dup(nvpair_hash, key, value);
     896             :         }
     897             :     }
     898             : 
     899           0 :     return nvpair_hash;
     900             : }
     901             : 
     902             : void
     903           2 : pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
     904             : {
     905           2 :     crm_xml_add(node, name, pcmk__btoa(value));
     906           2 : }
     907             : 
     908             : int
     909          50 : pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
     910             : {
     911          50 :     const char *xml_value = NULL;
     912             :     int ret, rc;
     913             : 
     914          50 :     if (node == NULL) {
     915           4 :         return ENODATA;
     916          46 :     } else if (name == NULL || value == NULL) {
     917           3 :         return EINVAL;
     918             :     }
     919             : 
     920          43 :     xml_value = crm_element_value(node, name);
     921             : 
     922          43 :     if (xml_value == NULL) {
     923           2 :         return ENODATA;
     924             :     }
     925             : 
     926          41 :     rc = crm_str_to_boolean(xml_value, &ret);
     927          41 :     if (rc == 1) {
     928          40 :         *value = ret;
     929          40 :         return pcmk_rc_ok;
     930             :     } else {
     931           1 :         return pcmk_rc_bad_input;
     932             :     }
     933             : }
     934             : 
     935             : bool
     936          42 : pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
     937             : {
     938          42 :     bool value = false;
     939             :     int rc;
     940             : 
     941          42 :     rc = pcmk__xe_get_bool_attr(node, name, &value);
     942          42 :     return rc == pcmk_rc_ok && value == true;
     943             : }
     944             : 
     945             : // Meta-attribute handling
     946             : 
     947             : /*!
     948             :  * \brief Get the environment variable equivalent of a meta-attribute name
     949             :  *
     950             :  * \param[in] attr_name  Name of meta-attribute
     951             :  *
     952             :  * \return Newly allocated string for \p attr_name with "CRM_meta_" prefix and
     953             :  *         underbars instead of dashes
     954             :  * \note This asserts on an invalid argument or memory allocation error, so
     955             :  *       callers can assume the result is non-NULL. The caller is responsible
     956             :  *       for freeing the result using free().
     957             :  */
     958             : char *
     959          10 : crm_meta_name(const char *attr_name)
     960             : {
     961          10 :     char *env_name = NULL;
     962             : 
     963          10 :     CRM_ASSERT(!pcmk__str_empty(attr_name));
     964             : 
     965           9 :     env_name = crm_strdup_printf(CRM_META "_%s", attr_name);
     966         194 :     for (char *c = env_name; *c != '\0'; ++c) {
     967         185 :         if (*c == '-') {
     968           4 :             *c = '_';
     969             :         }
     970             :     }
     971           9 :     return env_name;
     972             : }
     973             : 
     974             : /*!
     975             :  * \brief Get the value of a meta-attribute
     976             :  *
     977             :  * Get the value of a meta-attribute from a hash table whose keys are
     978             :  * meta-attribute environment variable names (as crm_meta_name() would
     979             :  * create, like pcmk__graph_action_t:params, not pcmk_resource_t:meta).
     980             :  *
     981             :  * \param[in] meta       Hash table of meta-attributes
     982             :  * \param[in] attr_name  Name of meta-attribute to get
     983             :  *
     984             :  * \return Value of given meta-attribute
     985             :  */
     986             : const char *
     987           6 : crm_meta_value(GHashTable *meta, const char *attr_name)
     988             : {
     989           6 :     if ((meta != NULL) && (attr_name != NULL)) {
     990           4 :         char *key = crm_meta_name(attr_name);
     991           4 :         const char *value = g_hash_table_lookup(meta, key);
     992             : 
     993           4 :         free(key);
     994           4 :         return value;
     995             :     }
     996           2 :     return NULL;
     997             : }
     998             : 
     999             : // Deprecated functions kept only for backward API compatibility
    1000             : // LCOV_EXCL_START
    1001             : 
    1002             : #include <crm/common/util_compat.h>
    1003             : 
    1004             : int
    1005             : pcmk_scan_nvpair(const char *input, char **name, char **value)
    1006             : {
    1007             :     return pcmk__scan_nvpair(input, name, value);
    1008             : }
    1009             : 
    1010             : char *
    1011             : pcmk_format_nvpair(const char *name, const char *value,
    1012             :                    const char *units)
    1013             : {
    1014             :     return pcmk__format_nvpair(name, value, units);
    1015             : }
    1016             : 
    1017             : char *
    1018             : pcmk_format_named_time(const char *name, time_t epoch_time)
    1019             : {
    1020             :     char *now_s = pcmk__epoch2str(&epoch_time, 0);
    1021             :     char *result = crm_strdup_printf("%s=\"%s\"", name, pcmk__s(now_s, ""));
    1022             : 
    1023             :     free(now_s);
    1024             :     return result;
    1025             : }
    1026             : 
    1027             : const char *
    1028             : crm_xml_replace(xmlNode *node, const char *name, const char *value)
    1029             : {
    1030             :     bool dirty = FALSE;
    1031             :     xmlAttr *attr = NULL;
    1032             :     const char *old_value = NULL;
    1033             : 
    1034             :     CRM_CHECK(node != NULL, return NULL);
    1035             :     CRM_CHECK(name != NULL && name[0] != 0, return NULL);
    1036             : 
    1037             :     old_value = crm_element_value(node, name);
    1038             : 
    1039             :     /* Could be re-setting the same value */
    1040             :     CRM_CHECK(old_value != value, return value);
    1041             : 
    1042             :     if (pcmk__check_acl(node, name, pcmk__xf_acl_write) == FALSE) {
    1043             :         /* Create a fake object linked to doc->_private instead? */
    1044             :         crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
    1045             :         return NULL;
    1046             : 
    1047             :     } else if (old_value && !value) {
    1048             :         pcmk__xe_remove_attr(node, name);
    1049             :         return NULL;
    1050             :     }
    1051             : 
    1052             :     if (pcmk__tracking_xml_changes(node, FALSE)) {
    1053             :         if (!old_value || !value || !strcmp(old_value, value)) {
    1054             :             dirty = TRUE;
    1055             :         }
    1056             :     }
    1057             : 
    1058             :     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
    1059             :     if (dirty) {
    1060             :         pcmk__mark_xml_attr_dirty(attr);
    1061             :     }
    1062             :     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
    1063             :     return (char *) attr->children->content;
    1064             : }
    1065             : 
    1066             : // LCOV_EXCL_STOP
    1067             : // End deprecated API

Generated by: LCOV version 1.14