pacemaker  1.1.24-3850484742
Scalable High-Availability cluster resource manager
nvpair.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 
10 #include <stdio.h>
11 #include <sys/types.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <glib.h>
15 #include <libxml/tree.h>
16 
17 #include <crm/crm.h>
18 #include <crm/msg_xml.h>
19 #include <crm/common/xml.h>
20 #include "crmcommon_private.h"
21 
22 /*
23  * This file isolates handling of three types of name/value pairs:
24  *
25  * - pcmk_nvpair_t data type
26  * - XML attributes (<TAG ... NAME=VALUE ...>)
27  * - XML nvpair elements (<nvpair id=ID name=NAME value=VALUE>)
28  */
29 
30 // pcmk_nvpair_t handling
31 
43 static pcmk_nvpair_t *
44 pcmk__new_nvpair(const char *name, const char *value)
45 {
46  pcmk_nvpair_t *nvpair = NULL;
47 
48  CRM_ASSERT(name);
49 
50  nvpair = calloc(1, sizeof(pcmk_nvpair_t));
51  CRM_ASSERT(nvpair);
52 
53  nvpair->name = strdup(name);
54  nvpair->value = value? strdup(value) : NULL;
55  return nvpair;
56 }
57 
64 static void
65 pcmk__free_nvpair(gpointer data)
66 {
67  if (data) {
68  pcmk_nvpair_t *nvpair = data;
69 
70  free(nvpair->name);
71  free(nvpair->value);
72  free(nvpair);
73  }
74 }
75 
87 GSList *
88 pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
89 {
90  return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
91 }
92 
98 void
99 pcmk_free_nvpairs(GSList *nvpairs)
100 {
101  g_slist_free_full(nvpairs, pcmk__free_nvpair);
102 }
103 
113 static gint
114 pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
115 {
116  int rc = 0;
117  const pcmk_nvpair_t *pair_a = a;
118  const pcmk_nvpair_t *pair_b = b;
119 
120  CRM_ASSERT(a != NULL);
121  CRM_ASSERT(pair_a->name != NULL);
122 
123  CRM_ASSERT(b != NULL);
124  CRM_ASSERT(pair_b->name != NULL);
125 
126  rc = strcmp(pair_a->name, pair_b->name);
127  if (rc < 0) {
128  return -1;
129  } else if (rc > 0) {
130  return 1;
131  }
132  return 0;
133 }
134 
142 GSList *
143 pcmk_sort_nvpairs(GSList *list)
144 {
145  return g_slist_sort(list, pcmk__compare_nvpair);
146 }
147 
157 GSList *
159 {
160  GSList *result = NULL;
161 
162  for (xmlAttrPtr iter = pcmk__first_xml_attr(xml); iter != NULL;
163  iter = iter->next) {
164 
165  result = pcmk_prepend_nvpair(result,
166  (const char *) iter->name,
167  (const char *) pcmk__xml_attr_value(iter));
168  }
169  return result;
170 }
171 
182 static void
183 pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
184 {
185  pcmk_nvpair_t *pair = data;
186  xmlNode *parent = user_data;
187 
188  crm_xml_add(parent, pair->name, pair->value);
189 }
190 
197 void
198 pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
199 {
200  g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
201 }
202 
203 // XML attribute handling
204 
215 const char *
216 crm_xml_add(xmlNode *node, const char *name, const char *value)
217 {
218  bool dirty = FALSE;
219  xmlAttr *attr = NULL;
220 
221  CRM_CHECK(node != NULL, return NULL);
222  CRM_CHECK(name != NULL, return NULL);
223 
224  if (value == NULL) {
225  return NULL;
226  }
227 #if XML_PARANOIA_CHECKS
228  {
229  const char *old_value = NULL;
230 
231  old_value = crm_element_value(node, name);
232 
233  /* Could be re-setting the same value */
234  CRM_CHECK(old_value != value,
235  crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
236  return value);
237  }
238 #endif
239 
240  if (pcmk__tracking_xml_changes(node, FALSE)) {
241  const char *old = crm_element_value(node, name);
242 
243  if (old == NULL || value == NULL || strcmp(old, value) != 0) {
244  dirty = TRUE;
245  }
246  }
247 
248  if (dirty && (pcmk__check_acl(node, name, xpf_acl_create) == FALSE)) {
249  crm_trace("Cannot add %s=%s to %s", name, value, node->name);
250  return NULL;
251  }
252 
253  attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
254  if (dirty) {
256  }
257 
258  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
259  return (char *)attr->children->content;
260 }
261 
272 const char *
273 crm_xml_replace(xmlNode *node, const char *name, const char *value)
274 {
275  bool dirty = FALSE;
276  xmlAttr *attr = NULL;
277  const char *old_value = NULL;
278 
279  CRM_CHECK(node != NULL, return NULL);
280  CRM_CHECK(name != NULL && name[0] != 0, return NULL);
281 
282  old_value = crm_element_value(node, name);
283 
284  /* Could be re-setting the same value */
285  CRM_CHECK(old_value != value, return value);
286 
287  if (pcmk__check_acl(node, name, xpf_acl_write) == FALSE) {
288  /* Create a fake object linked to doc->_private instead? */
289  crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
290  return NULL;
291 
292  } else if (old_value && !value) {
293  xml_remove_prop(node, name);
294  return NULL;
295  }
296 
297  if (pcmk__tracking_xml_changes(node, FALSE)) {
298  if (!old_value || !value || !strcmp(old_value, value)) {
299  dirty = TRUE;
300  }
301  }
302 
303  attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
304  if (dirty) {
306  }
307  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
308  return (char *) attr->children->content;
309 }
310 
323 const char *
324 crm_xml_add_int(xmlNode *node, const char *name, int value)
325 {
326  char *number = crm_itoa(value);
327  const char *added = crm_xml_add(node, name, number);
328 
329  free(number);
330  return added;
331 }
332 
345 const char *
346 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
347 {
348  char *number = crm_strdup_printf("%u", ms);
349  const char *added = crm_xml_add(node, name, number);
350 
351  free(number);
352  return added;
353 }
354 
355 // Maximum size of null-terminated string representation of 64-bit integer
356 // -9223372036854775808
357 #define LLSTRSIZE 21
358 
373 const char *
374 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
375 {
376  char s[LLSTRSIZE] = { '\0', };
377 
378  if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
379  return NULL;
380  }
381  return crm_xml_add(xml, name, s);
382 }
383 
392 const char *
393 crm_element_value(const xmlNode *data, const char *name)
394 {
395  xmlAttr *attr = NULL;
396 
397  if (data == NULL) {
398  crm_err("Couldn't find %s in NULL", name ? name : "<null>");
399  CRM_LOG_ASSERT(data != NULL);
400  return NULL;
401 
402  } else if (name == NULL) {
403  crm_err("Couldn't find NULL in %s", crm_element_name(data));
404  return NULL;
405  }
406 
407  /* The first argument to xmlHasProp() has always been const,
408  * but libxml2 <2.9.2 didn't declare that, so cast it
409  */
410  attr = xmlHasProp((xmlNode *) data, (const xmlChar *)name);
411  if (!attr || !attr->children) {
412  return NULL;
413  }
414  return (const char *) attr->children->content;
415 }
416 
427 int
428 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
429 {
430  const char *value = NULL;
431 
432  CRM_CHECK(dest != NULL, return -1);
433  value = crm_element_value(data, name);
434  if (value) {
435  *dest = crm_int_helper(value, NULL);
436  return 0;
437  }
438  return -1;
439 }
440 
452 int
453 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
454 {
455  const char *value = NULL;
456 
457  CRM_CHECK(dest != NULL, return -1);
458  value = crm_element_value(data, name);
459  if (value) {
460  errno = 0;
461  *dest = crm_int_helper(value, NULL);
462  if (errno == 0) {
463  return 0;
464  }
465  }
466  return -1;
467 }
468 
469 int
470 crm_element_value_const_int(const xmlNode * data, const char *name, int *dest)
471 {
472  return crm_element_value_int((xmlNode *) data, name, dest);
473 }
474 
475 const char *
476 crm_element_value_const(const xmlNode * data, const char *name)
477 {
478  return crm_element_value((xmlNode *) data, name);
479 }
480 
492 int
493 crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
494 {
495  long long value_ll = 0;
496 
497  if (crm_element_value_ll(xml, name, &value_ll) < 0) {
498  return -1;
499  }
500 
501  /* Unfortunately, we can't do any bounds checking, since time_t has neither
502  * standardized bounds nor constants defined for them.
503  */
504  *dest = (time_t) value_ll;
505  return pcmk_ok;
506 }
507 
521 int
522 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
523  const char *name_usec, struct timeval *dest)
524 {
525  const char *value_s = NULL;
526  long long value_i = 0;
527 
528  CRM_CHECK(dest != NULL, return -EINVAL);
529  dest->tv_sec = 0;
530  dest->tv_usec = 0;
531 
532  if (xml == NULL) {
533  return 0;
534  }
535 
536  // Parse seconds
537  value_s = crm_element_value(xml, name_sec);
538  if (value_s) {
539  value_i = crm_parse_ll(value_s, NULL);
540  if (errno) {
541  return -errno;
542  }
543  dest->tv_sec = (time_t) value_i;
544  }
545 
546  // Parse microseconds
547  value_s = crm_element_value(xml, name_usec);
548  if (value_s) {
549  value_i = crm_parse_ll(value_s, NULL);
550  if (errno) {
551  return -errno;
552  }
553  dest->tv_usec = (suseconds_t) value_i;
554  }
555  return 0;
556 }
557 
569 char *
570 crm_element_value_copy(const xmlNode *data, const char *name)
571 {
572  char *value_copy = NULL;
573  const char *value = crm_element_value(data, name);
574 
575  if (value != NULL) {
576  value_copy = strdup(value);
577  }
578  return value_copy;
579 }
580 
594 void
595 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
596 {
597  const char *name = key;
598  const char *s_value = value;
599 
600  xmlNode *xml_node = user_data;
601 
602  if (isdigit(name[0])) {
603  xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
604 
605  crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
606  crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
607 
608  } else if (crm_element_value(xml_node, name) == NULL) {
609  crm_xml_add(xml_node, name, s_value);
610  crm_trace("dumped: %s=%s", name, s_value);
611 
612  } else {
613  crm_trace("duplicate: %s=%s", name, s_value);
614  }
615 }
616 
628 void
629 hash2field(gpointer key, gpointer value, gpointer user_data)
630 {
631  const char *name = key;
632  const char *s_value = value;
633 
634  xmlNode *xml_node = user_data;
635 
636  if (crm_element_value(xml_node, name) == NULL) {
637  crm_xml_add(xml_node, name, s_value);
638 
639  } else {
640  crm_trace("duplicate: %s=%s", name, s_value);
641  }
642 }
643 
656 void
657 hash2metafield(gpointer key, gpointer value, gpointer user_data)
658 {
659  char *crm_name = NULL;
660 
661  if (key == NULL || value == NULL) {
662  return;
663  }
664 
665  /* Filter out cluster-generated attributes that contain a '#' or ':'
666  * (like fail-count and last-failure).
667  */
668  for (crm_name = key; *crm_name; ++crm_name) {
669  if ((*crm_name == '#') || (*crm_name == ':')) {
670  return;
671  }
672  }
673 
674  crm_name = crm_meta_name(key);
675  hash2field(crm_name, value, user_data);
676  free(crm_name);
677 }
678 
679 // nvpair handling
680 
691 xmlNode *
692 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
693  const char *value)
694 {
695  xmlNode *nvp;
696 
697  /* id can be NULL so we auto-generate one, and name can be NULL if this
698  * will be used to delete a name/value pair by ID, but both can't be NULL
699  */
700  CRM_CHECK(id || name, return NULL);
701 
702  nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
703  CRM_CHECK(nvp, return NULL);
704 
705  if (id) {
706  crm_xml_add(nvp, XML_ATTR_ID, id);
707  } else {
708  const char *parent_id = ID(parent);
709 
710  crm_xml_set_id(nvp, "%s-%s",
711  (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
712  }
713  crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
714  crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
715  return nvp;
716 }
717 
729 void
730 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
731 {
732  const char *name = key;
733  const char *s_value = value;
734  xmlNode *xml_node = user_data;
735 
736  crm_create_nvpair_xml(xml_node, name, name, s_value);
737  crm_trace("dumped: name=%s value=%s", name, s_value);
738 }
739 
754 GHashTable *
755 xml2list(xmlNode *parent)
756 {
757  xmlNode *child = NULL;
758  xmlAttrPtr pIter = NULL;
759  xmlNode *nvpair_list = NULL;
760  GHashTable *nvpair_hash = crm_str_table_new();
761 
762  CRM_CHECK(parent != NULL, return nvpair_hash);
763 
764  nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
765  if (nvpair_list == NULL) {
766  crm_trace("No attributes in %s", crm_element_name(parent));
767  crm_log_xml_trace(parent, "No attributes for resource op");
768  }
769 
770  crm_log_xml_trace(nvpair_list, "Unpacking");
771 
772  for (pIter = pcmk__first_xml_attr(nvpair_list); pIter != NULL;
773  pIter = pIter->next) {
774 
775  const char *p_name = (const char *)pIter->name;
776  const char *p_value = pcmk__xml_attr_value(pIter);
777 
778  crm_trace("Added %s=%s", p_name, p_value);
779 
780  g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
781  }
782 
783  for (child = __xml_first_child(nvpair_list); child != NULL;
784  child = __xml_next(child)) {
785 
786  if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
787  const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
788  const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
789 
790  crm_trace("Added %s=%s", key, value);
791  if (key != NULL && value != NULL) {
792  g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
793  }
794  }
795  }
796 
797  return nvpair_hash;
798 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:190
xmlNode * find_xml_node(xmlNode *cib, const char *node_path, gboolean must_find)
Definition: xml.c:1765
GSList * pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
Prepend a name/value pair to a list.
Definition: nvpair.c:88
A dumping ground.
char * name
Definition: nvpair.h:27
int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
Retrieve the long long integer value of an XML attribute.
Definition: nvpair.c:453
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:158
char * value
Definition: nvpair.h:28
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:205
#define pcmk_ok
Definition: error.h:45
long long crm_int_helper(const char *text, char **end_text)
Definition: strings.c:81
int crm_element_value_const_int(const xmlNode *data, const char *name, int *dest)
Definition: nvpair.c:470
#define XML_TAG_ATTRS
Definition: msg_xml.h:187
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:370
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:176
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:182
xmlNode * crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name, const char *value)
Create an XML name/value pair.
Definition: nvpair.c:692
char * crm_meta_name(const char *field)
Definition: utils.c:935
const char * crm_element_value_const(const xmlNode *data, const char *name)
Definition: nvpair.c:476
int crm_element_value_timeval(const xmlNode *xml, const char *name_sec, const char *name_usec, struct timeval *dest)
Retrieve the value of XML second/microsecond attributes as time.
Definition: nvpair.c:522
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:592
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:657
GHashTable * xml2list(xmlNode *parent)
Retrieve XML attributes as a hash table.
Definition: nvpair.c:755
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:198
#define XML_ATTR_ID
Definition: msg_xml.h:102
#define crm_trace(fmt, args...)
Definition: logging.h:280
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:629
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:99
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:595
Wrappers for and extensions to libxml2.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1977
#define LLSTRSIZE
Definition: nvpair.c:357
void hash2nvpair(gpointer key, gpointer value, gpointer user_data)
Add XML nvpair element based on hash table entry.
Definition: nvpair.c:730
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:143
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:393
const char * crm_xml_replace(xmlNode *node, const char *name, const char *value)
Replace an XML attribute with specified name and (possibly NULL) value.
Definition: nvpair.c:273
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:134
#define crm_err(fmt, args...)
Definition: logging.h:274
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:324
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
Definition: nvpair.c:493
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:3239
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:216
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:371
const char * crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
Create an XML attribute with specified name and long long int value.
Definition: nvpair.c:374
#define CRM_ASSERT(expr)
Definition: error.h:20
char data[0]
Definition: internal.h:86
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:570
#define crm_log_xml_trace(xml, text)
Definition: logging.h:288
const char * crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
Create an XML attribute with specified name and unsigned value.
Definition: nvpair.c:346
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:86
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:428
#define ID(x)
Definition: msg_xml.h:452
char * crm_itoa(int an_int)
Definition: strings.c:61
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define XML_TAG_PARAM
Definition: msg_xml.h:192