pacemaker  2.0.2-debe490
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 // convenience function for name=value strings
204 
216 int
217 pcmk_scan_nvpair(const char *input, char **name, char **value)
218 {
219 #ifdef SSCANF_HAS_M
220  *name = NULL;
221  *value = NULL;
222  if (sscanf(input, "%m[^=]=%m[^\n]", name, value) <= 0) {
223  return -pcmk_err_bad_nvpair;
224  }
225 #else
226  char *sep = NULL;
227  *name = NULL;
228  *value = NULL;
229 
230  sep = strstr(optarg, "=");
231  if (sep == NULL) {
232  return -pcmk_err_bad_nvpair;
233  }
234 
235  *name = strndup(input, sep-input);
236 
237  if (*name == NULL) {
238  return -ENOMEM;
239  }
240 
241  /* If the last char in optarg is =, the user gave no
242  * value for the option. Leave it as NULL.
243  */
244  if (*(sep+1) != '\0') {
245  *value = strdup(sep+1);
246 
247  if (*value == NULL) {
248  return -ENOMEM;
249  }
250  }
251 #endif
252 
253  if (*name != NULL && *value != NULL) {
254  return 2;
255  } else if (*name != NULL || *value != NULL) {
256  return 1;
257  } else {
258  return -pcmk_err_bad_nvpair;
259  }
260 }
261 
262 // XML attribute handling
263 
274 const char *
275 crm_xml_add(xmlNode *node, const char *name, const char *value)
276 {
277  bool dirty = FALSE;
278  xmlAttr *attr = NULL;
279 
280  CRM_CHECK(node != NULL, return NULL);
281  CRM_CHECK(name != NULL, return NULL);
282 
283  if (value == NULL) {
284  return NULL;
285  }
286 #if XML_PARANOIA_CHECKS
287  {
288  const char *old_value = NULL;
289 
290  old_value = crm_element_value(node, name);
291 
292  /* Could be re-setting the same value */
293  CRM_CHECK(old_value != value,
294  crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
295  return value);
296  }
297 #endif
298 
299  if (pcmk__tracking_xml_changes(node, FALSE)) {
300  const char *old = crm_element_value(node, name);
301 
302  if (old == NULL || value == NULL || strcmp(old, value) != 0) {
303  dirty = TRUE;
304  }
305  }
306 
307  if (dirty && (pcmk__check_acl(node, name, xpf_acl_create) == FALSE)) {
308  crm_trace("Cannot add %s=%s to %s", name, value, node->name);
309  return NULL;
310  }
311 
312  attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
313  if (dirty) {
315  }
316 
317  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
318  return (char *)attr->children->content;
319 }
320 
331 const char *
332 crm_xml_replace(xmlNode *node, const char *name, const char *value)
333 {
334  bool dirty = FALSE;
335  xmlAttr *attr = NULL;
336  const char *old_value = NULL;
337 
338  CRM_CHECK(node != NULL, return NULL);
339  CRM_CHECK(name != NULL && name[0] != 0, return NULL);
340 
341  old_value = crm_element_value(node, name);
342 
343  /* Could be re-setting the same value */
344  CRM_CHECK(old_value != value, return value);
345 
346  if (pcmk__check_acl(node, name, xpf_acl_write) == FALSE) {
347  /* Create a fake object linked to doc->_private instead? */
348  crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
349  return NULL;
350 
351  } else if (old_value && !value) {
352  xml_remove_prop(node, name);
353  return NULL;
354  }
355 
356  if (pcmk__tracking_xml_changes(node, FALSE)) {
357  if (!old_value || !value || !strcmp(old_value, value)) {
358  dirty = TRUE;
359  }
360  }
361 
362  attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
363  if (dirty) {
365  }
366  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
367  return (char *) attr->children->content;
368 }
369 
382 const char *
383 crm_xml_add_int(xmlNode *node, const char *name, int value)
384 {
385  char *number = crm_itoa(value);
386  const char *added = crm_xml_add(node, name, number);
387 
388  free(number);
389  return added;
390 }
391 
404 const char *
405 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
406 {
407  char *number = crm_strdup_printf("%u", ms);
408  const char *added = crm_xml_add(node, name, number);
409 
410  free(number);
411  return added;
412 }
413 
422 const char *
423 crm_element_value(const xmlNode *data, const char *name)
424 {
425  xmlAttr *attr = NULL;
426 
427  if (data == NULL) {
428  crm_err("Couldn't find %s in NULL", name ? name : "<null>");
429  CRM_LOG_ASSERT(data != NULL);
430  return NULL;
431 
432  } else if (name == NULL) {
433  crm_err("Couldn't find NULL in %s", crm_element_name(data));
434  return NULL;
435  }
436 
437  /* The first argument to xmlHasProp() has always been const,
438  * but libxml2 <2.9.2 didn't declare that, so cast it
439  */
440  attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
441  if (!attr || !attr->children) {
442  return NULL;
443  }
444  return (const char *) attr->children->content;
445 }
446 
458 int
459 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
460 {
461  const char *value = NULL;
462 
463  CRM_CHECK(dest != NULL, return -1);
464  value = crm_element_value(data, name);
465  if (value) {
466  *dest = crm_int_helper(value, NULL);
467  return 0;
468  }
469  return -1;
470 }
471 
483 int
484 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
485 {
486  const char *value = NULL;
487 
488  CRM_CHECK(dest != NULL, return -1);
489  value = crm_element_value(data, name);
490  *dest = crm_parse_ms(value);
491  return errno? -1 : 0;
492 }
493 
507 int
508 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
509  const char *name_usec, struct timeval *dest)
510 {
511  const char *value_s = NULL;
512  long long value_i = 0;
513 
514  CRM_CHECK(dest != NULL, return -EINVAL);
515  dest->tv_sec = 0;
516  dest->tv_usec = 0;
517 
518  if (xml == NULL) {
519  return 0;
520  }
521 
522  // Parse seconds
523  value_s = crm_element_value(xml, name_sec);
524  if (value_s) {
525  value_i = crm_parse_ll(value_s, NULL);
526  if (errno) {
527  return -errno;
528  }
529  dest->tv_sec = (time_t) value_i;
530  }
531 
532  // Parse microseconds
533  value_s = crm_element_value(xml, name_usec);
534  if (value_s) {
535  value_i = crm_parse_ll(value_s, NULL);
536  if (errno) {
537  return -errno;
538  }
539  dest->tv_usec = (suseconds_t) value_i;
540  }
541  return 0;
542 }
543 
555 char *
556 crm_element_value_copy(const xmlNode *data, const char *name)
557 {
558  char *value_copy = NULL;
559  const char *value = crm_element_value(data, name);
560 
561  if (value != NULL) {
562  value_copy = strdup(value);
563  }
564  return value_copy;
565 }
566 
580 void
581 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
582 {
583  const char *name = key;
584  const char *s_value = value;
585 
586  xmlNode *xml_node = user_data;
587 
588  if (isdigit(name[0])) {
589  xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
590 
591  crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
592  crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
593 
594  } else if (crm_element_value(xml_node, name) == NULL) {
595  crm_xml_add(xml_node, name, s_value);
596  crm_trace("dumped: %s=%s", name, s_value);
597 
598  } else {
599  crm_trace("duplicate: %s=%s", name, s_value);
600  }
601 }
602 
614 void
615 hash2field(gpointer key, gpointer value, gpointer user_data)
616 {
617  const char *name = key;
618  const char *s_value = value;
619 
620  xmlNode *xml_node = user_data;
621 
622  if (crm_element_value(xml_node, name) == NULL) {
623  crm_xml_add(xml_node, name, s_value);
624 
625  } else {
626  crm_trace("duplicate: %s=%s", name, s_value);
627  }
628 }
629 
642 void
643 hash2metafield(gpointer key, gpointer value, gpointer user_data)
644 {
645  char *crm_name = NULL;
646 
647  if (key == NULL || value == NULL) {
648  return;
649  }
650 
651  /* Filter out cluster-generated attributes that contain a '#' or ':'
652  * (like fail-count and last-failure).
653  */
654  for (crm_name = key; *crm_name; ++crm_name) {
655  if ((*crm_name == '#') || (*crm_name == ':')) {
656  return;
657  }
658  }
659 
660  crm_name = crm_meta_name(key);
661  hash2field(crm_name, value, user_data);
662  free(crm_name);
663 }
664 
665 // nvpair handling
666 
677 xmlNode *
678 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
679  const char *value)
680 {
681  xmlNode *nvp;
682 
683  /* id can be NULL so we auto-generate one, and name can be NULL if this
684  * will be used to delete a name/value pair by ID, but both can't be NULL
685  */
686  CRM_CHECK(id || name, return NULL);
687 
688  nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
689  CRM_CHECK(nvp, return NULL);
690 
691  if (id) {
692  crm_xml_add(nvp, XML_ATTR_ID, id);
693  } else {
694  const char *parent_id = ID(parent);
695 
696  crm_xml_set_id(nvp, "%s-%s",
697  (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
698  }
699  crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
700  crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
701  return nvp;
702 }
703 
715 void
716 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
717 {
718  const char *name = key;
719  const char *s_value = value;
720  xmlNode *xml_node = user_data;
721 
722  crm_create_nvpair_xml(xml_node, name, name, s_value);
723  crm_trace("dumped: name=%s value=%s", name, s_value);
724 }
725 
740 GHashTable *
741 xml2list(xmlNode *parent)
742 {
743  xmlNode *child = NULL;
744  xmlAttrPtr pIter = NULL;
745  xmlNode *nvpair_list = NULL;
746  GHashTable *nvpair_hash = crm_str_table_new();
747 
748  CRM_CHECK(parent != NULL, return nvpair_hash);
749 
750  nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
751  if (nvpair_list == NULL) {
752  crm_trace("No attributes in %s", crm_element_name(parent));
753  crm_log_xml_trace(parent, "No attributes for resource op");
754  }
755 
756  crm_log_xml_trace(nvpair_list, "Unpacking");
757 
758  for (pIter = pcmk__first_xml_attr(nvpair_list); pIter != NULL;
759  pIter = pIter->next) {
760 
761  const char *p_name = (const char *)pIter->name;
762  const char *p_value = pcmk__xml_attr_value(pIter);
763 
764  crm_trace("Added %s=%s", p_name, p_value);
765 
766  g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
767  }
768 
769  for (child = __xml_first_child(nvpair_list); child != NULL;
770  child = __xml_next(child)) {
771 
772  if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
773  const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
774  const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
775 
776  crm_trace("Added %s=%s", key, value);
777  if (key != NULL && value != NULL) {
778  g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
779  }
780  }
781  }
782 
783  return nvpair_hash;
784 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:156
xmlNode * find_xml_node(xmlNode *cib, const char *node_path, gboolean must_find)
Definition: xml.c:1678
A dumping ground.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:143
char * name
Definition: nvpair.h:29
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:615
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:99
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:405
char * value
Definition: nvpair.h:30
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:183
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:383
long long crm_int_helper(const char *text, char **end_text)
Definition: strings.c:34
#define XML_TAG_ATTRS
Definition: msg_xml.h:165
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:275
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:339
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:142
GSList * pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
Prepend a name/value pair to a list.
Definition: nvpair.c:88
int pcmk_scan_nvpair(const char *input, char **name, char **value)
Extract the name and value from an input string formatted as &quot;name=value&quot;. If unable to extract them...
Definition: nvpair.c:217
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:160
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:158
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:459
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:581
char * strndup(const char *str, size_t len)
char * crm_meta_name(const char *field)
Definition: utils.c:734
void hash2nvpair(gpointer key, gpointer value, gpointer user_data)
Add XML nvpair element based on hash table entry.
Definition: nvpair.c:716
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:557
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:484
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:556
#define XML_ATTR_ID
Definition: msg_xml.h:96
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:423
int crm_element_value_timeval(const xmlNode *data, const char *name_sec, const char *name_usec, struct timeval *dest)
Retrieve the value of XML second/microsecond attributes as time.
Definition: nvpair.c:508
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:332
#define crm_trace(fmt, args...)
Definition: logging.h:246
Wrappers for and extensions to libxml2.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1890
const xmlChar * pcmkXmlStr
Definition: xml.h:51
guint crm_parse_ms(const char *text)
Definition: strings.c:143
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:87
#define crm_err(fmt, args...)
Definition: logging.h:240
#define CRM_ASSERT(expr)
Definition: results.h:42
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:3215
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:340
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:643
char data[0]
Definition: internal.h:92
GHashTable * xml2list(xmlNode *parent)
Retrieve XML attributes as a hash table.
Definition: nvpair.c:741
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:198
#define crm_log_xml_trace(xml, text)
Definition: logging.h:254
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:64
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:678
#define ID(x)
Definition: msg_xml.h:414
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define pcmk_err_bad_nvpair
Definition: results.h:75
#define XML_TAG_PARAM
Definition: msg_xml.h:170