pacemaker  2.0.1-9e909a5bdd
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 = calloc(1, sizeof(pcmk_nvpair_t));
47 
48  CRM_ASSERT(nvpair);
49  nvpair->name = strdup(name);
50  nvpair->value = strdup(value);
51  return nvpair;
52 }
53 
60 static void
61 pcmk__free_nvpair(gpointer data)
62 {
63  if (data) {
64  pcmk_nvpair_t *nvpair = data;
65 
66  free(nvpair->name);
67  free(nvpair->value);
68  free(nvpair);
69  }
70 }
71 
83 GSList *
84 pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
85 {
86  return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
87 }
88 
94 void
95 pcmk_free_nvpairs(GSList *nvpairs)
96 {
97  g_slist_free_full(nvpairs, pcmk__free_nvpair);
98 }
99 
109 static gint
110 pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
111 {
112  int rc = 0;
113  const pcmk_nvpair_t *pair_a = a;
114  const pcmk_nvpair_t *pair_b = b;
115 
116  CRM_ASSERT(a != NULL);
117  CRM_ASSERT(pair_a->name != NULL);
118 
119  CRM_ASSERT(b != NULL);
120  CRM_ASSERT(pair_b->name != NULL);
121 
122  rc = strcmp(pair_a->name, pair_b->name);
123  if (rc < 0) {
124  return -1;
125  } else if (rc > 0) {
126  return 1;
127  }
128  return 0;
129 }
130 
138 GSList *
139 pcmk_sort_nvpairs(GSList *list)
140 {
141  return g_slist_sort(list, pcmk__compare_nvpair);
142 }
143 
153 GSList *
155 {
156  GSList *result = NULL;
157 
158  for (xmlAttrPtr iter = pcmk__first_xml_attr(xml); iter != NULL;
159  iter = iter->next) {
160 
161  result = pcmk_prepend_nvpair(result,
162  (const char *) iter->name,
163  (const char *) pcmk__xml_attr_value(iter));
164  }
165  return result;
166 }
167 
178 static void
179 pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
180 {
181  pcmk_nvpair_t *pair = data;
182  xmlNode *parent = user_data;
183 
184  crm_xml_add(parent, pair->name, pair->value);
185 }
186 
193 void
194 pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
195 {
196  g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
197 }
198 
199 // XML attribute handling
200 
211 const char *
212 crm_xml_add(xmlNode *node, const char *name, const char *value)
213 {
214  bool dirty = FALSE;
215  xmlAttr *attr = NULL;
216 
217  CRM_CHECK(node != NULL, return NULL);
218  CRM_CHECK(name != NULL, return NULL);
219 
220  if (value == NULL) {
221  return NULL;
222  }
223 #if XML_PARANOIA_CHECKS
224  {
225  const char *old_value = NULL;
226 
227  old_value = crm_element_value(node, name);
228 
229  /* Could be re-setting the same value */
230  CRM_CHECK(old_value != value,
231  crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
232  return value);
233  }
234 #endif
235 
236  if (pcmk__tracking_xml_changes(node, FALSE)) {
237  const char *old = crm_element_value(node, name);
238 
239  if (old == NULL || value == NULL || strcmp(old, value) != 0) {
240  dirty = TRUE;
241  }
242  }
243 
244  if (dirty && (pcmk__check_acl(node, name, xpf_acl_create) == FALSE)) {
245  crm_trace("Cannot add %s=%s to %s", name, value, node->name);
246  return NULL;
247  }
248 
249  attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
250  if (dirty) {
252  }
253 
254  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
255  return (char *)attr->children->content;
256 }
257 
268 const char *
269 crm_xml_replace(xmlNode *node, const char *name, const char *value)
270 {
271  bool dirty = FALSE;
272  xmlAttr *attr = NULL;
273  const char *old_value = NULL;
274 
275  CRM_CHECK(node != NULL, return NULL);
276  CRM_CHECK(name != NULL && name[0] != 0, return NULL);
277 
278  old_value = crm_element_value(node, name);
279 
280  /* Could be re-setting the same value */
281  CRM_CHECK(old_value != value, return value);
282 
283  if (pcmk__check_acl(node, name, xpf_acl_write) == FALSE) {
284  /* Create a fake object linked to doc->_private instead? */
285  crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
286  return NULL;
287 
288  } else if (old_value && !value) {
289  xml_remove_prop(node, name);
290  return NULL;
291  }
292 
293  if (pcmk__tracking_xml_changes(node, FALSE)) {
294  if (!old_value || !value || !strcmp(old_value, value)) {
295  dirty = TRUE;
296  }
297  }
298 
299  attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
300  if (dirty) {
302  }
303  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
304  return (char *) attr->children->content;
305 }
306 
319 const char *
320 crm_xml_add_int(xmlNode *node, const char *name, int value)
321 {
322  char *number = crm_itoa(value);
323  const char *added = crm_xml_add(node, name, number);
324 
325  free(number);
326  return added;
327 }
328 
341 const char *
342 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
343 {
344  char *number = crm_strdup_printf("%u", ms);
345  const char *added = crm_xml_add(node, name, number);
346 
347  free(number);
348  return added;
349 }
350 
359 const char *
360 crm_element_value(const xmlNode *data, const char *name)
361 {
362  xmlAttr *attr = NULL;
363 
364  if (data == NULL) {
365  crm_err("Couldn't find %s in NULL", name ? name : "<null>");
366  CRM_LOG_ASSERT(data != NULL);
367  return NULL;
368 
369  } else if (name == NULL) {
370  crm_err("Couldn't find NULL in %s", crm_element_name(data));
371  return NULL;
372  }
373 
374  /* The first argument to xmlHasProp() has always been const,
375  * but libxml2 <2.9.2 didn't declare that, so cast it
376  */
377  attr = xmlHasProp((xmlNode *) data, (const xmlChar *)name);
378  if (!attr || !attr->children) {
379  return NULL;
380  }
381  return (const char *) attr->children->content;
382 }
383 
394 int
395 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
396 {
397  const char *value = NULL;
398 
399  CRM_CHECK(dest != NULL, return -1);
400  value = crm_element_value(data, name);
401  if (value) {
402  *dest = crm_int_helper(value, NULL);
403  return 0;
404  }
405  return -1;
406 }
407 
419 int
420 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
421 {
422  const char *value = NULL;
423 
424  CRM_CHECK(dest != NULL, return -1);
425  value = crm_element_value(data, name);
426  *dest = crm_parse_ms(value);
427  return errno? -1 : 0;
428 }
429 
443 int
444 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
445  const char *name_usec, struct timeval *dest)
446 {
447  const char *value_s = NULL;
448  long long value_i = 0;
449 
450  CRM_CHECK(dest != NULL, return -EINVAL);
451  dest->tv_sec = 0;
452  dest->tv_usec = 0;
453 
454  if (xml == NULL) {
455  return 0;
456  }
457 
458  // Parse seconds
459  value_s = crm_element_value(xml, name_sec);
460  if (value_s) {
461  value_i = crm_parse_ll(value_s, NULL);
462  if (errno) {
463  return -errno;
464  }
465  dest->tv_sec = (time_t) value_i;
466  }
467 
468  // Parse microseconds
469  value_s = crm_element_value(xml, name_usec);
470  if (value_s) {
471  value_i = crm_parse_ll(value_s, NULL);
472  if (errno) {
473  return -errno;
474  }
475  dest->tv_usec = (suseconds_t) value_i;
476  }
477  return 0;
478 }
479 
491 char *
492 crm_element_value_copy(const xmlNode *data, const char *name)
493 {
494  char *value_copy = NULL;
495  const char *value = crm_element_value(data, name);
496 
497  if (value != NULL) {
498  value_copy = strdup(value);
499  }
500  return value_copy;
501 }
502 
516 void
517 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
518 {
519  const char *name = key;
520  const char *s_value = value;
521 
522  xmlNode *xml_node = user_data;
523 
524  if (isdigit(name[0])) {
525  xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
526 
527  crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
528  crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
529 
530  } else if (crm_element_value(xml_node, name) == NULL) {
531  crm_xml_add(xml_node, name, s_value);
532  crm_trace("dumped: %s=%s", name, s_value);
533 
534  } else {
535  crm_trace("duplicate: %s=%s", name, s_value);
536  }
537 }
538 
550 void
551 hash2field(gpointer key, gpointer value, gpointer user_data)
552 {
553  const char *name = key;
554  const char *s_value = value;
555 
556  xmlNode *xml_node = user_data;
557 
558  if (crm_element_value(xml_node, name) == NULL) {
559  crm_xml_add(xml_node, name, s_value);
560 
561  } else {
562  crm_trace("duplicate: %s=%s", name, s_value);
563  }
564 }
565 
578 void
579 hash2metafield(gpointer key, gpointer value, gpointer user_data)
580 {
581  char *crm_name = NULL;
582 
583  if (key == NULL || value == NULL) {
584  return;
585  }
586 
587  /* Filter out cluster-generated attributes that contain a '#' or ':'
588  * (like fail-count and last-failure).
589  */
590  for (crm_name = key; *crm_name; ++crm_name) {
591  if ((*crm_name == '#') || (*crm_name == ':')) {
592  return;
593  }
594  }
595 
596  crm_name = crm_meta_name(key);
597  hash2field(crm_name, value, user_data);
598  free(crm_name);
599 }
600 
601 // nvpair handling
602 
613 xmlNode *
614 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
615  const char *value)
616 {
617  xmlNode *nvp;
618 
619  /* id can be NULL so we auto-generate one, and name can be NULL if this
620  * will be used to delete a name/value pair by ID, but both can't be NULL
621  */
622  CRM_CHECK(id || name, return NULL);
623 
624  nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
625  CRM_CHECK(nvp, return NULL);
626 
627  if (id) {
628  crm_xml_add(nvp, XML_ATTR_ID, id);
629  } else {
630  const char *parent_id = ID(parent);
631 
632  crm_xml_set_id(nvp, "%s-%s",
633  (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
634  }
635  crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
636  crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
637  return nvp;
638 }
639 
651 void
652 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
653 {
654  const char *name = key;
655  const char *s_value = value;
656  xmlNode *xml_node = user_data;
657 
658  crm_create_nvpair_xml(xml_node, name, name, s_value);
659  crm_trace("dumped: name=%s value=%s", name, s_value);
660 }
661 
676 GHashTable *
677 xml2list(xmlNode *parent)
678 {
679  xmlNode *child = NULL;
680  xmlAttrPtr pIter = NULL;
681  xmlNode *nvpair_list = NULL;
682  GHashTable *nvpair_hash = crm_str_table_new();
683 
684  CRM_CHECK(parent != NULL, return nvpair_hash);
685 
686  nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
687  if (nvpair_list == NULL) {
688  crm_trace("No attributes in %s", crm_element_name(parent));
689  crm_log_xml_trace(parent, "No attributes for resource op");
690  }
691 
692  crm_log_xml_trace(nvpair_list, "Unpacking");
693 
694  for (pIter = pcmk__first_xml_attr(nvpair_list); pIter != NULL;
695  pIter = pIter->next) {
696 
697  const char *p_name = (const char *)pIter->name;
698  const char *p_value = pcmk__xml_attr_value(pIter);
699 
700  crm_trace("Added %s=%s", p_name, p_value);
701 
702  g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
703  }
704 
705  for (child = __xml_first_child(nvpair_list); child != NULL;
706  child = __xml_next(child)) {
707 
708  if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
709  const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
710  const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
711 
712  crm_trace("Added %s=%s", key, value);
713  if (key != NULL && value != NULL) {
714  g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
715  }
716  }
717  }
718 
719  return nvpair_hash;
720 }
crm_element_value_ms
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:420
msg_xml.h
xpf_acl_write
Definition: crmcommon_private.h:29
data
char data[0]
Definition: internal.h:90
create_xml_node
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1888
crm_xml_add
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:212
crm_element_value_copy
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:492
crm_element_value_timeval
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:444
CRM_CHECK
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:165
crm_parse_ms
guint crm_parse_ms(const char *text)
Definition: strings.c:127
crm_err
#define crm_err(fmt, args...)
Definition: logging.h:249
crm_trace
#define crm_trace(fmt, args...)
Definition: logging.h:255
XML_NVPAIR_ATTR_VALUE
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:338
pcmk_sort_nvpairs
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:139
crm_xml_add_int
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:320
xml.h
Wrappers for and extensions to libxml2.
hash2field
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:551
pcmk_free_nvpairs
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:95
crm_parse_ll
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:85
XML_ATTR_ID
#define XML_ATTR_ID
Definition: msg_xml.h:94
ID
#define ID(x)
Definition: msg_xml.h:412
hash2nvpair
void hash2nvpair(gpointer key, gpointer value, gpointer user_data)
Add XML nvpair element based on hash table entry.
Definition: nvpair.c:652
crm_xml_set_id
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
CRM_LOG_ASSERT
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:151
crm_strdup_printf
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
crmcommon_private.h
XML_TAG_ATTRS
#define XML_TAG_ATTRS
Definition: msg_xml.h:163
crm_xml_replace
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:269
pcmk__tracking_xml_changes
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:62
xml2list
GHashTable * xml2list(xmlNode *parent)
Retrieve XML attributes as a hash table.
Definition: nvpair.c:677
crm_log_xml_trace
#define crm_log_xml_trace(xml, text)
Definition: logging.h:263
xpf_acl_create
Definition: crmcommon_private.h:32
pcmk_xml_attrs2nvpairs
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node's attributes.
Definition: nvpair.c:154
crm_xml_add_ms
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:342
crm_element_value_int
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:395
xml_remove_prop
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:3183
pcmk_prepend_nvpair
GSList * pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
Prepend a name/value pair to a list.
Definition: nvpair.c:84
hash2smartfield
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:517
pcmk__mark_xml_attr_dirty
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:181
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:20
find_xml_node
xmlNode * find_xml_node(xmlNode *cib, const char *node_path, gboolean must_find)
Definition: xml.c:1676
XML_TAG_PARAM
#define XML_TAG_PARAM
Definition: msg_xml.h:168
pcmk_nvpair_s
Definition: nvpair.h:26
XML_CIB_TAG_NVPAIR
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:158
crm_element_value
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:360
crm_create_nvpair_xml
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:614
pcmk__check_acl
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:557
hash2metafield
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:579
crm_internal.h
pcmk_nvpairs2xml_attrs
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:194
pcmk_nvpair_s::value
char * value
Definition: nvpair.h:28
crm.h
A dumping ground.
crm_meta_name
char * crm_meta_name(const char *field)
Definition: utils.c:732
XML_NVPAIR_ATTR_NAME
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:337
pcmk_nvpair_s::name
char * name
Definition: nvpair.h:27
crm_int_helper
long long crm_int_helper(const char *text, char **end_text)
Definition: strings.c:32