13 #include <sys/types.h>
21 #include <libxml/parser.h>
22 #include <libxml/tree.h>
23 #include <libxml/xmlIO.h>
32 #define XML_BUFFER_SIZE 4096
33 #define XML_PARSER_DEBUG 0
44 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER)
51 typedef struct xml_deleted_obj_s {
58 static filter_t filter[] = {
67 static xmlNode *subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right, gboolean * changed);
68 static xmlNode *find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact);
69 static int add_xml_comment(xmlNode * parent, xmlNode *
target, xmlNode * update);
71 #define CHUNK_SIZE 1024
76 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
80 }
else if (lazy && is_not_set(((
xml_private_t *)xml->doc->_private)->flags,
87 #define buffer_print(buffer, max, offset, fmt, args...) do { \
90 rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \
92 if(buffer && rc < 0) { \
93 crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \
94 (buffer)[(offset)] = 0; \
96 } else if(rc >= ((max) - (offset))) { \
98 (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \
99 tmp = realloc_safe((buffer), (max)); \
109 insert_prefix(
int options,
char **buffer,
int *offset,
int *max,
int depth)
112 size_t spaces = 2 * depth;
114 if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
116 (*buffer) = realloc_safe((*buffer), (*max));
118 memset((*buffer) + (*offset),
' ', spaces);
124 set_parent_flag(xmlNode *xml,
long flag)
127 for(; xml; xml = xml->parent) {
143 if(xml && xml->doc && xml->doc->_private){
153 __xml_node_dirty(xmlNode *xml)
160 __xml_node_clean(xmlNode *xml)
162 xmlNode *cIter = NULL;
169 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
170 __xml_node_clean(cIter);
175 crm_node_created(xmlNode *xml)
177 xmlNode *cIter = NULL;
183 __xml_node_dirty(xml);
186 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
187 crm_node_created(cIter);
195 xmlNode *parent = a->parent;
204 __xml_node_dirty(parent);
212 static int add_xml_object(xmlNode * parent, xmlNode *
target, xmlNode * update, gboolean as_diff);
214 #define XML_PRIVATE_MAGIC (long) 0x81726354
217 __xml_deleted_obj_free(
void *
data)
222 free(deleted_obj->path);
242 g_list_free_full(p->
deleted_objs, __xml_deleted_obj_free);
252 __xml_private_clean(p);
257 pcmkDeregisterNode(xmlNodePtr node)
266 if (node->type != XML_DOCUMENT_NODE || node->name == NULL
267 || node->name[0] !=
' ') {
268 __xml_private_free(node->_private);
273 pcmkRegisterNode(xmlNodePtr node)
278 case XML_ELEMENT_NODE:
279 case XML_DOCUMENT_NODE:
280 case XML_ATTRIBUTE_NODE:
281 case XML_COMMENT_NODE:
290 case XML_CDATA_SECTION_NODE:
294 crm_trace(
"Ignoring %p %d", node, node->type);
304 __xml_node_dirty(node);
312 crm_trace(
"Tracking changes%s to %p", enforce_acls?
" with ACLs":
"", xml);
315 if(acl_source == NULL) {
337 if(xml != NULL && xml->doc && xml->doc->_private) {
393 static int __xml_offset(xmlNode *xml)
396 xmlNode *cIter = NULL;
398 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
409 static int __xml_offset_no_deletions(xmlNode *xml)
412 xmlNode *cIter = NULL;
414 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
426 __xml_build_changes(xmlNode * xml, xmlNode *patchset)
428 xmlNode *cIter = NULL;
429 xmlAttr *pIter = NULL;
430 xmlNode *change = NULL;
438 sizeof(buffer)) > 0) {
439 int position = __xml_offset_no_deletions(xml);
452 for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
453 xmlNode *attr = NULL;
465 sizeof(buffer)) > 0) {
490 xmlNode *result = NULL;
495 for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
500 crm_xml_add(result, (
const char *)pIter->name, value);
505 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
506 __xml_build_changes(cIter, patchset);
514 crm_trace(
"%s.%s moved to position %d", xml->name,
ID(xml), __xml_offset(xml));
516 sizeof(buffer)) > 0) {
527 __xml_accept_changes(xmlNode * xml)
529 xmlNode *cIter = NULL;
530 xmlAttr *pIter = NULL;
534 pIter = pcmk__first_xml_attr(xml);
536 while (pIter != NULL) {
537 const xmlChar *
name = pIter->name;
550 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
551 __xml_accept_changes(cIter);
556 is_config_change(xmlNode *xml)
563 p = config->_private;
569 if(xml->doc && xml->doc->_private) {
570 p = xml->doc->_private;
571 for(gIter = p->
deleted_objs; gIter; gIter = gIter->next) {
584 xml_repair_v1_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
588 xmlNode *diff_child = NULL;
590 const char *tag = NULL;
592 const char *vfields[] = {
598 if (local_diff == NULL) {
603 tag =
"diff-removed";
605 if (diff_child == NULL) {
615 for(lpc = 0; last && lpc <
DIMOF(vfields); lpc++){
619 if(changed || lpc == 2) {
626 if (diff_child == NULL) {
636 for(lpc = 0; next && lpc <
DIMOF(vfields); lpc++){
643 xmlAttrPtr xIter = NULL;
645 for (xIter = next->properties; xIter; xIter = xIter->next) {
646 const char *p_name = (
const char *)xIter->name;
657 xml_create_patchset_v1(xmlNode *source, xmlNode *
target,
bool config,
bool suppress)
663 xml_repair_v1_diff(source,
target, patchset, config);
670 xml_create_patchset_v2(xmlNode *source, xmlNode *
target)
678 xmlNode *patchset = NULL;
679 const char *vfields[] = {
691 doc =
target->doc->_private;
699 for(lpc = 0; lpc <
DIMOF(vfields); lpc++){
709 for(lpc = 0; lpc <
DIMOF(vfields); lpc++){
718 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
724 if (deleted_obj->position >= 0) {
729 __xml_build_changes(
target, patchset);
738 xmlNode *patch = NULL;
747 config = is_config_change(
target);
749 *config_changed = config;
752 if(manage_version && config) {
759 }
else if(manage_version) {
777 patch = xml_create_patchset_v1(source,
target, config, FALSE);
780 patch = xml_create_patchset_v2(source,
target);
783 crm_err(
"Unknown patch format: %d", format);
797 if (patch == NULL || source == NULL ||
target == NULL) {
806 if (format > 1 && with_digest == FALSE) {
820 __xml_log_element(
int log_level,
const char *file,
const char *
function,
int line,
821 const char *prefix, xmlNode *
data,
int depth,
int options);
827 xmlNode *child = NULL;
828 xmlNode *added = NULL;
829 xmlNode *removed = NULL;
830 gboolean is_first = TRUE;
832 int add[] = { 0, 0, 0 };
833 int del[] = { 0, 0, 0 };
835 const char *fmt = NULL;
836 const char *digest = NULL;
839 static struct qb_log_callsite *patchset_cs = NULL;
844 if (patchset_cs == NULL) {
845 patchset_cs = qb_log_callsite_get(
function, __FILE__,
"xml-patchset", log_level, __LINE__, 0);
848 if (patchset == NULL) {
861 if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
863 "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
865 "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
867 }
else if (patchset != NULL && (add[0] || add[1] || add[2])) {
869 "%s: Local-only Change: %d.%d.%d",
function ?
function :
"",
870 add[0], add[1], add[2]);
875 xmlNode *change = NULL;
877 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
882 }
else if(strcmp(op,
"create") == 0) {
883 int lpc = 0, max = 0;
886 max = strlen(prefix);
887 __xml_log_element(log_level, __FILE__,
function, __LINE__, prefix, change->children,
890 for(lpc = 2; lpc < max; lpc++) {
894 __xml_log_element(log_level, __FILE__,
function, __LINE__, prefix, change->children,
898 }
else if(strcmp(op,
"move") == 0) {
901 }
else if(strcmp(op,
"modify") == 0) {
910 for (child = __xml_first_child(clist); child != NULL; child = __xml_next(child)) {
915 }
else if(strcmp(op,
"set") == 0) {
923 }
else if(strcmp(op,
"unset") == 0) {
925 o_unset += snprintf(buffer_unset + o_unset,
XML_BUFFER_SIZE - o_unset,
", ");
931 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"+ %s: %s", xpath, buffer_set);
934 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"-- %s: %s", xpath, buffer_unset);
937 }
else if(strcmp(op,
"delete") == 0) {
942 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"-- %s (%d)", xpath, position);
952 if (log_level < LOG_DEBUG ||
function == NULL) {
957 for (child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
969 for (child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
993 doc = xml->doc->_private;
998 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
1001 if (deleted_obj->position >= 0) {
1003 deleted_obj->path, deleted_obj->position);
1018 xmlNode *top = NULL;
1025 crm_trace(
"Accepting changes to %p", xml);
1026 doc = xml->doc->_private;
1027 top = xmlDocGetRootElement(xml->doc);
1029 __xml_private_clean(xml->doc->_private);
1037 __xml_accept_changes(top);
1041 find_element(xmlNode *haystack, xmlNode *needle, gboolean exact)
1044 return (needle->type == XML_COMMENT_NODE)?
1045 find_xml_comment(haystack, needle, exact)
1046 :
find_entity(haystack, crm_element_name(needle),
ID(needle));
1051 __subtract_xml_object(xmlNode *
target, xmlNode * patch)
1053 xmlNode *patch_child = NULL;
1054 xmlNode *cIter = NULL;
1055 xmlAttrPtr xIter = NULL;
1058 const char *
name = NULL;
1059 const char *value = NULL;
1061 if (
target == NULL || patch == NULL) {
1065 if (
target->type == XML_COMMENT_NODE) {
1068 subtract_xml_comment(
target->parent,
target, patch, &dummy);
1079 if (value != NULL && strcmp(value,
"removed:top") == 0) {
1080 crm_trace(
"We are the root of the deletion: %s.id=%s",
name,
id);
1086 for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1087 const char *p_name = (
const char *)xIter->name;
1096 cIter = __xml_first_child(
target);
1098 xmlNode *target_child = cIter;
1100 cIter = __xml_next(cIter);
1101 patch_child = find_element(patch, target_child, FALSE);
1102 __subtract_xml_object(target_child, patch_child);
1108 __add_xml_object(xmlNode * parent, xmlNode *
target, xmlNode * patch)
1110 xmlNode *patch_child = NULL;
1111 xmlNode *target_child = NULL;
1112 xmlAttrPtr xIter = NULL;
1114 const char *
id = NULL;
1115 const char *
name = NULL;
1116 const char *value = NULL;
1118 if (patch == NULL) {
1120 }
else if (parent == NULL &&
target == NULL) {
1128 && strcmp(value,
"added:top") == 0) {
1130 name = crm_element_name(patch);
1131 crm_trace(
"We are the root of the addition: %s.id=%s",
name,
id);
1135 }
else if(
target == NULL) {
1137 name = crm_element_name(patch);
1142 if (
target->type == XML_COMMENT_NODE) {
1143 add_xml_comment(parent,
target, patch);
1151 for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1152 const char *p_name = (
const char *)xIter->name;
1160 for (patch_child = __xml_first_child(patch); patch_child != NULL;
1161 patch_child = __xml_next(patch_child)) {
1163 target_child = find_element(
target, patch_child, FALSE);
1164 __add_xml_object(
target, target_child, patch_child);
1180 find_patch_xml_node(xmlNode *patchset,
int format,
bool added,
1181 xmlNode **patch_node)
1188 label = added?
"diff-added" :
"diff-removed";
1191 if (cib_node != NULL) {
1192 *patch_node = cib_node;
1196 label = added?
"target" :
"source";
1201 crm_warn(
"Unknown patch format: %d", format);
1212 xmlNode *tmp = NULL;
1214 const char *vfields[] = {
1224 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
1228 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1230 crm_trace(
"Got %d for del[%s]", del[lpc], vfields[lpc]);
1235 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
1239 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1241 crm_trace(
"Got %d for add[%s]", add[lpc], vfields[lpc]);
1249 xml_patch_version_check(xmlNode *xml, xmlNode *patchset,
int format)
1252 bool changed = FALSE;
1254 int this[] = { 0, 0, 0 };
1255 int add[] = { 0, 0, 0 };
1256 int del[] = { 0, 0, 0 };
1258 const char *vfields[] = {
1264 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1266 crm_trace(
"Got %d for this[%s]",
this[lpc], vfields[lpc]);
1267 if (
this[lpc] < 0) {
1275 add[2] =
this[2] + 1;
1276 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1277 del[lpc] =
this[lpc];
1282 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1283 if(
this[lpc] < del[lpc]) {
1284 crm_debug(
"Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)", vfields[lpc],
1285 this[0],
this[1],
this[2], del[0], del[1], del[2], add[0], add[1], add[2]);
1288 }
else if(
this[lpc] > del[lpc]) {
1289 crm_info(
"Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p", vfields[lpc],
1290 this[0],
this[1],
this[2], del[0], del[1], del[2], add[0], add[1], add[2], patchset);
1296 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1297 if(add[lpc] > del[lpc]) {
1302 if(changed == FALSE) {
1303 crm_notice(
"Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
1307 crm_debug(
"Can apply patch %d.%d.%d to %d.%d.%d",
1308 add[0], add[1], add[2],
this[0],
this[1],
this[2]);
1313 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset)
1316 int root_nodes_seen = 0;
1318 xmlNode *child_diff = NULL;
1319 xmlNode *added =
find_xml_node(patchset,
"diff-added", FALSE);
1320 xmlNode *removed =
find_xml_node(patchset,
"diff-removed", FALSE);
1324 for (child_diff = __xml_first_child(removed); child_diff != NULL;
1325 child_diff = __xml_next(child_diff)) {
1327 if (root_nodes_seen == 0) {
1328 __subtract_xml_object(xml, child_diff);
1333 if (root_nodes_seen > 1) {
1334 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1338 root_nodes_seen = 0;
1341 xmlNode *child_diff = NULL;
1343 for (child_diff = __xml_first_child(added); child_diff != NULL;
1344 child_diff = __xml_next(child_diff)) {
1346 if (root_nodes_seen == 0) {
1347 __add_xml_object(NULL, xml, child_diff);
1353 if (root_nodes_seen > 1) {
1354 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1365 __first_xml_child_match(xmlNode *parent,
const char *
name,
const char *
id,
int position)
1367 xmlNode *cIter = NULL;
1369 for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
1370 if(strcmp((
const char *)cIter->name,
name) != 0) {
1373 const char *cid =
ID(cIter);
1374 if(cid == NULL || strcmp(cid,
id) != 0) {
1380 if (cIter->type == XML_COMMENT_NODE
1382 && __xml_offset(cIter) != position) {
1405 __xml_find_path(xmlNode *top,
const char *key,
int target_position)
1407 xmlNode *
target = (xmlNode*) top->doc;
1408 const char *current = key;
1418 key_len = strlen(key);
1424 remainder = calloc(key_len,
sizeof(
char));
1427 section = calloc(key_len,
sizeof(
char));
1430 id = calloc(key_len,
sizeof(
char));
1433 tag = calloc(key_len,
sizeof(
char));
1438 rc = sscanf(current,
"/%[^/]%s", section, remainder);
1441 int f = sscanf(section,
"%[^[][@id='%[^']", tag,
id);
1442 int current_position = -1;
1447 if ((
rc == 1) && (target_position >= 0)) {
1448 current_position = target_position;
1453 target = __first_xml_child_match(
target, tag, NULL, current_position);
1456 target = __first_xml_child_match(
target, tag,
id, current_position);
1463 current = remainder;
1471 (path = (
char *) xmlGetNodePath(
target)), key);
1484 typedef struct xml_change_obj_s {
1490 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
1494 int position_a = -1;
1495 int position_b = -1;
1500 if (position_a < position_b) {
1503 }
else if (position_a > position_b) {
1511 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
1514 xmlNode *change = NULL;
1518 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
1519 xmlNode *match = NULL;
1528 crm_trace(
"Processing %s %s", change->name, op);
1531 if(strcmp(op,
"delete") == 0) {
1534 match = __xml_find_path(xml, xpath, position);
1535 crm_trace(
"Performing %s on %s with %p", op, xpath, match);
1537 if(match == NULL && strcmp(op,
"delete") == 0) {
1538 crm_debug(
"No %s match for %s in %p", op, xpath, xml->doc);
1541 }
else if(match == NULL) {
1542 crm_err(
"No %s match for %s in %p", op, xpath, xml->doc);
1546 }
else if (strcmp(op,
"create") == 0 || strcmp(op,
"move") == 0) {
1552 change_obj->change = change;
1553 change_obj->match = match;
1555 change_objs = g_list_append(change_objs, change_obj);
1557 if (strcmp(op,
"move") == 0) {
1559 if (match->parent != NULL && match->parent->last != NULL) {
1560 xmlAddNextSibling(match->parent->last, match);
1564 }
else if(strcmp(op,
"delete") == 0) {
1567 }
else if(strcmp(op,
"modify") == 0) {
1568 xmlAttr *pIter = pcmk__first_xml_attr(match);
1575 while(pIter != NULL) {
1576 const char *
name = (
const char *)pIter->name;
1578 pIter = pIter->next;
1582 for (pIter = pcmk__first_xml_attr(attrs); pIter != NULL; pIter = pIter->next) {
1583 const char *
name = (
const char *)pIter->name;
1590 crm_err(
"Unknown operation: %s", op);
1596 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
1598 for (gIter = change_objs; gIter; gIter = gIter->next) {
1600 xmlNode *match = change_obj->match;
1601 const char *op = NULL;
1602 const char *xpath = NULL;
1604 change = change_obj->change;
1609 crm_trace(
"Continue performing %s on %s with %p", op, xpath, match);
1611 if(strcmp(op,
"create") == 0) {
1613 xmlNode *child = NULL;
1614 xmlNode *match_child = NULL;
1616 match_child = match->children;
1619 while(match_child && position != __xml_offset(match_child)) {
1620 match_child = match_child->next;
1623 child = xmlDocCopyNode(change->children, match->doc, 1);
1625 crm_trace(
"Adding %s at position %d", child->name, position);
1626 xmlAddPrevSibling(match_child, child);
1628 }
else if(match->last) {
1629 crm_trace(
"Adding %s at position %d (end)", child->name, position);
1630 xmlAddNextSibling(match->last, child);
1633 crm_trace(
"Adding %s at position %d (first)", child->name, position);
1635 xmlAddChild(match, child);
1637 crm_node_created(child);
1639 }
else if(strcmp(op,
"move") == 0) {
1643 if(position != __xml_offset(match)) {
1644 xmlNode *match_child = NULL;
1647 if(p > __xml_offset(match)) {
1652 match_child = match->parent->children;
1654 while(match_child && p != __xml_offset(match_child)) {
1655 match_child = match_child->next;
1658 crm_trace(
"Moving %s to position %d (was %d, prev %p, %s %p)",
1659 match->name, position, __xml_offset(match), match->prev,
1660 match_child?
"next":
"last", match_child?match_child:match->parent->last);
1663 xmlAddPrevSibling(match_child, match);
1667 xmlAddNextSibling(match->parent->last, match);
1671 crm_trace(
"%s is already in position %d", match->name, position);
1674 if(position != __xml_offset(match)) {
1675 crm_err(
"Moved %s.%s to position %d instead of %d (%p)",
1676 match->name,
ID(match), __xml_offset(match), position, match->prev);
1682 g_list_free_full(change_objs, free);
1691 xmlNode *old = NULL;
1694 if(patchset == NULL) {
1702 rc = xml_patch_version_check(xml, patchset, format);
1716 rc = xml_apply_patchset_v1(xml, patchset);
1719 rc = xml_apply_patchset_v2(xml, patchset);
1722 crm_err(
"Unknown patch format: %d", format);
1728 static struct qb_log_callsite *digest_cs = NULL;
1730 char *new_digest = NULL;
1733 if (digest_cs == NULL) {
1735 qb_log_callsite_get(__func__, __FILE__,
"diff-digest",
LOG_TRACE, __LINE__,
1741 crm_info(
"v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
1744 if (digest_cs && digest_cs->targets) {
1750 crm_trace(
"%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
1754 crm_trace(
"v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
1766 xmlNode *a_child = NULL;
1767 const char *
name =
"NULL";
1770 name = crm_element_name(root);
1773 if (search_path == NULL) {
1774 crm_warn(
"Will never find <NULL>");
1778 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
1779 if (strcmp((
const char *)a_child->name, search_path) == 0) {
1786 crm_warn(
"Could not find %s in %s.", search_path,
name);
1787 }
else if (root != NULL) {
1790 crm_trace(
"Could not find %s in <NULL>.", search_path);
1800 find_entity_by_attr_or_just_name(xmlNode *parent,
const char *node_name,
1801 const char *attr_n,
const char *attr_v)
1806 CRM_CHECK(attr_n == NULL || attr_v != NULL,
return NULL);
1808 for (child = __xml_first_child(parent); child != NULL; child = __xml_next(child)) {
1810 if (node_name == NULL || !strcmp((
const char *) child->name, node_name)) {
1820 attr_n ? attr_n :
"",
1822 attr_n ? attr_v :
"",
1823 crm_element_name(parent));
1831 return find_entity_by_attr_or_just_name(parent, node_name,
1839 crm_warn(
"No node to copy properties from");
1841 }
else if (
target == NULL) {
1842 crm_err(
"No node to copy properties into");
1845 xmlAttrPtr pIter = NULL;
1847 for (pIter = pcmk__first_xml_attr(src); pIter != NULL; pIter = pIter->next) {
1848 const char *p_name = (
const char *)pIter->name;
1849 const char *p_value = pcmk__xml_attr_value(pIter);
1862 xmlNode *child = NULL;
1863 xmlAttrPtr pIter = NULL;
1865 for (pIter = pcmk__first_xml_attr(
target); pIter != NULL; pIter = pIter->next) {
1866 const char *p_name = (
const char *)pIter->name;
1867 const char *p_value = pcmk__xml_attr_value(pIter);
1871 for (child = __xml_first_child(
target); child != NULL; child = __xml_next(child)) {
1884 const char *old_value = NULL;
1886 if (value == NULL ||
name == NULL) {
1892 if (old_value == NULL) {
1894 goto set_unexpanded;
1896 }
else if (strstr(value,
name) != value) {
1897 goto set_unexpanded;
1900 name_len = strlen(
name);
1901 value_len = strlen(value);
1902 if (value_len < (name_len + 2)
1903 || value[name_len] !=
'+' || (value[name_len + 1] !=
'+' && value[name_len + 1] !=
'=')) {
1904 goto set_unexpanded;
1910 if (old_value != value) {
1914 if (value[name_len + 1] !=
'+') {
1915 const char *offset_s = value + (name_len + 2);
1919 int_value += offset;
1929 if (old_value == value) {
1947 xmlDocSetRootElement(doc, node);
1948 xmlSetTreeDoc(node, doc);
1956 xmlNode *child = NULL;
1959 CRM_CHECK(src_node != NULL,
return NULL);
1961 child = xmlDocCopyNode(src_node, doc, 1);
1962 xmlAddChild(parent, child);
1963 crm_node_created(child);
1979 xmlNode *node = NULL;
1981 if (
name == NULL ||
name[0] == 0) {
1986 if (parent == NULL) {
1989 xmlDocSetRootElement(doc, node);
1994 xmlAddChild(parent, node);
1996 crm_node_created(node);
2006 xmlNodeSetContent(node, (
pcmkXmlStr) content);
2014 const char *class_name,
const char *text)
2018 if (class_name != NULL) {
2031 int offset,
size_t buffer_size)
2033 const char *
id =
ID(xml);
2035 if(offset == 0 && prefix == NULL && xml->parent) {
2041 offset += snprintf(buffer + offset, buffer_size - offset,
2042 "/%s[@id='%s']", (
const char *) xml->name,
id);
2043 }
else if(xml->name) {
2044 offset += snprintf(buffer + offset, buffer_size - offset,
2045 "/%s", (
const char *) xml->name);
2058 return strdup(buffer);
2076 free_xml_with_position(xmlNode * child,
int position)
2078 if (child != NULL) {
2079 xmlNode *top = NULL;
2080 xmlDoc *doc = child->doc;
2084 top = xmlDocGetRootElement(doc);
2087 if (doc != NULL && top == child) {
2106 sizeof(buffer)) > 0) {
2109 crm_trace(
"Deleting %s %p from %p", buffer, child, doc);
2111 deleted_obj->path = strdup(buffer);
2113 deleted_obj->position = -1;
2115 if (child->type == XML_COMMENT_NODE) {
2116 if (position >= 0) {
2117 deleted_obj->position = position;
2120 deleted_obj->position = __xml_offset(child);
2138 free_xml_with_position(child, -1);
2145 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
2147 xmlDocSetRootElement(doc, copy);
2148 xmlSetTreeDoc(copy, doc);
2153 crm_xml_err(
void *ctx,
const char *fmt, ...)
2154 G_GNUC_PRINTF(2, 3);
2157 crm_xml_err(
void *ctx, const
char *fmt, ...)
2160 static struct qb_log_callsite *xml_error_cs = NULL;
2162 if (xml_error_cs == NULL) {
2163 xml_error_cs = qb_log_callsite_get(
2168 if (xml_error_cs && xml_error_cs->targets) {
2170 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__,
"xml library error",
2172 "XML Error: ", fmt, ap);
2182 xmlNode *xml = NULL;
2183 xmlDocPtr output = NULL;
2184 xmlParserCtxtPtr ctxt = NULL;
2185 xmlErrorPtr last_error = NULL;
2187 if (input == NULL) {
2188 crm_err(
"Can't parse NULL input");
2193 ctxt = xmlNewParserCtxt();
2196 xmlCtxtResetLastError(ctxt);
2197 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2198 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
2201 xml = xmlDocGetRootElement(output);
2203 last_error = xmlCtxtGetLastError(ctxt);
2204 if (last_error && last_error->code != XML_ERR_OK) {
2210 crm_warn(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
2211 last_error->domain, last_error->level, last_error->code, last_error->message);
2213 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
2216 }
else if (last_error->code != XML_ERR_DOCUMENT_END) {
2217 crm_err(
"Couldn't%s parse %d chars: %s", xml ?
" fully" :
"", (
int)strlen(input),
2224 int len = strlen(input);
2228 crm_warn(
"Parse error[+%.3d]: %.80s", lpc, input+lpc);
2236 xmlFreeParserCtxt(ctxt);
2243 size_t data_length = 0;
2244 size_t read_chars = 0;
2246 char *xml_buffer = NULL;
2247 xmlNode *xml_obj = NULL;
2251 read_chars = fread(xml_buffer + data_length, 1,
XML_BUFFER_SIZE, stdin);
2252 data_length += read_chars;
2255 if (data_length == 0) {
2256 crm_warn(
"No XML supplied on stdin");
2261 xml_buffer[data_length] =
'\0';
2270 decompress_file(
const char *filename)
2272 char *buffer = NULL;
2276 size_t length = 0, read_len = 0;
2278 BZFILE *bz_file = NULL;
2279 FILE *input = fopen(filename,
"r");
2281 if (input == NULL) {
2282 crm_perror(LOG_ERR,
"Could not open %s for reading", filename);
2286 bz_file = BZ2_bzReadOpen(&
rc, input, 0, 0, NULL, 0);
2288 crm_err(
"Could not prepare to read compressed %s: %s "
2290 BZ2_bzReadClose(&
rc, bz_file);
2297 while (
rc == BZ_OK) {
2301 crm_trace(
"Read %ld bytes from file: %d", (
long)read_len,
rc);
2303 if (
rc == BZ_OK ||
rc == BZ_STREAM_END) {
2308 buffer[length] =
'\0';
2310 if (
rc != BZ_STREAM_END) {
2311 crm_err(
"Could not read compressed %s: %s "
2317 BZ2_bzReadClose(&
rc, bz_file);
2321 crm_err(
"Could not read compressed %s: not built with bzlib support",
2330 xmlNode *iter = xml->children;
2333 xmlNode *next = iter->next;
2335 switch (iter->type) {
2341 case XML_ELEMENT_NODE:
2358 xmlNode *xml = NULL;
2359 xmlDocPtr output = NULL;
2360 gboolean uncompressed = TRUE;
2361 xmlParserCtxtPtr ctxt = NULL;
2362 xmlErrorPtr last_error = NULL;
2365 ctxt = xmlNewParserCtxt();
2368 xmlCtxtResetLastError(ctxt);
2369 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2375 if (filename == NULL) {
2377 output = xmlCtxtReadFd(ctxt, STDIN_FILENO,
"unknown.xml", NULL,
2380 }
else if (uncompressed) {
2384 char *input = decompress_file(filename);
2386 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
2391 if (output && (xml = xmlDocGetRootElement(output))) {
2395 last_error = xmlCtxtGetLastError(ctxt);
2396 if (last_error && last_error->code != XML_ERR_OK) {
2402 crm_err(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
2403 last_error->domain, last_error->level, last_error->code, last_error->message);
2405 if (last_error && last_error->code != XML_ERR_OK) {
2406 crm_err(
"Couldn't%s parse %s", xml ?
" fully" :
"", filename);
2413 xmlFreeParserCtxt(ctxt);
2431 now_str ? now_str :
"Could not determine current time");
2444 for (c =
id; *c; ++c) {
2469 va_start(ap, format);
2470 len = vasprintf(&
id, format, ap);
2491 write_xml_stream(xmlNode * xml_node,
const char *filename, FILE * stream, gboolean compress)
2494 char *buffer = NULL;
2495 unsigned int out = 0;
2508 unsigned int in = 0;
2509 BZFILE *bz_file = NULL;
2511 bz_file = BZ2_bzWriteOpen(&
rc, stream, 5, 0, 30);
2513 crm_warn(
"Not compressing %s: could not prepare file stream: %s "
2516 BZ2_bzWrite(&
rc, bz_file, buffer, strlen(buffer));
2518 crm_warn(
"Not compressing %s: could not compress data: %s "
2519 CRM_XS " bzerror=%d errno=%d",
2525 BZ2_bzWriteClose(&
rc, bz_file, 0, &in, &out);
2527 crm_warn(
"Not compressing %s: could not write compressed data: %s "
2528 CRM_XS " bzerror=%d errno=%d",
2533 crm_trace(
"Compressed XML for %s from %u bytes to %u",
2538 crm_warn(
"Not compressing %s: not built with bzlib support", filename);
2543 res = fprintf(stream,
"%s", buffer);
2553 if (fflush(stream) != 0) {
2555 crm_perror(LOG_ERR,
"flushing %s", filename);
2559 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
2561 crm_perror(LOG_ERR,
"synchronizing %s", filename);
2566 crm_trace(
"Saved %d bytes%s to %s as XML",
2567 res, ((out > 0)?
" (compressed)" :
""), filename);
2584 write_xml_fd(xmlNode * xml_node,
const char *filename,
int fd, gboolean compress)
2586 FILE *stream = NULL;
2588 CRM_CHECK(xml_node && (fd > 0),
return -EINVAL);
2589 stream = fdopen(fd,
"w");
2590 if (stream == NULL) {
2593 return write_xml_stream(xml_node, filename, stream, compress);
2608 FILE *stream = NULL;
2610 CRM_CHECK(xml_node && filename,
return -EINVAL);
2611 stream = fopen(filename,
"w");
2612 if (stream == NULL) {
2615 return write_xml_stream(xml_node, filename, stream, compress);
2623 return __xml_first_child(tmp);
2636 crm_xml_escape_shuffle(
char *text,
int start,
int *length,
const char *replace)
2639 int offset = strlen(replace) - 1;
2642 text = realloc_safe(text, *length);
2644 for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
2645 text[lpc] = text[lpc - offset];
2648 memcpy(text + start, replace, offset + 1);
2657 int length = 1 + strlen(text);
2658 char *copy = strdup(text);
2675 for (index = 0; index < length; index++) {
2676 switch (copy[index]) {
2680 copy = crm_xml_escape_shuffle(copy, index, &length,
"<");
2684 copy = crm_xml_escape_shuffle(copy, index, &length,
">");
2688 copy = crm_xml_escape_shuffle(copy, index, &length,
""");
2692 copy = crm_xml_escape_shuffle(copy, index, &length,
"'");
2696 copy = crm_xml_escape_shuffle(copy, index, &length,
"&");
2701 copy = crm_xml_escape_shuffle(copy, index, &length,
" ");
2706 copy = crm_xml_escape_shuffle(copy, index, &length,
"\\n");
2710 copy = crm_xml_escape_shuffle(copy, index, &length,
"\\r");
2720 if(copy[index] <
' ' || copy[index] >
'~') {
2724 copy = crm_xml_escape_shuffle(copy, index, &length, replace);
2738 dump_xml_attr(xmlAttrPtr attr,
int options,
char **buffer,
int *offset,
int *max)
2740 char *p_value = NULL;
2741 const char *p_name = NULL;
2745 if (attr == NULL || attr->children == NULL) {
2754 p_name = (
const char *)attr->name;
2756 buffer_print(*buffer, *max, *offset,
" %s=\"%s\"", p_name, p_value);
2761 __xml_log_element(
int log_level,
const char *file,
const char *
function,
int line,
2762 const char *prefix, xmlNode *
data,
int depth,
int options)
2766 const char *
name = NULL;
2767 const char *hidden = NULL;
2769 xmlNode *child = NULL;
2770 xmlAttrPtr pIter = NULL;
2779 char *buffer = NULL;
2781 insert_prefix(options, &buffer, &offset, &max, depth);
2783 if (
data->type == XML_COMMENT_NODE) {
2790 for (pIter = pcmk__first_xml_attr(
data); pIter != NULL; pIter = pIter->next) {
2792 const char *p_name = (
const char *)pIter->name;
2793 const char *p_value = pcmk__xml_attr_value(pIter);
2794 char *p_copy = NULL;
2803 }
else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
2804 p_copy = strdup(
"*****");
2810 buffer_print(buffer, max, offset,
" %s=\"%s\"", p_name, p_copy);
2825 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
2829 if(
data->type == XML_COMMENT_NODE) {
2839 for (child = __xml_first_child(
data); child != NULL; child = __xml_next(child)) {
2845 char *buffer = NULL;
2847 insert_prefix(options, &buffer, &offset, &max, depth);
2850 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
2856 __xml_log_change_element(
int log_level,
const char *file,
const char *
function,
int line,
2857 const char *prefix, xmlNode *
data,
int depth,
int options)
2860 char *prefix_m = NULL;
2861 xmlNode *child = NULL;
2862 xmlAttrPtr pIter = NULL;
2870 prefix_m = strdup(prefix);
2875 __xml_log_element(log_level, file,
function, line,
2879 char *spaces = calloc(80, 1);
2880 int s_count = 0, s_max = 80;
2881 char *prefix_del = NULL;
2882 char *prefix_moved = NULL;
2883 const char *
flags = prefix;
2885 insert_prefix(options, &spaces, &s_count, &s_max, depth);
2886 prefix_del = strdup(prefix);
2887 prefix_del[0] =
'-';
2888 prefix_del[1] =
'-';
2889 prefix_moved = strdup(prefix);
2890 prefix_moved[1] =
'~';
2893 flags = prefix_moved;
2898 __xml_log_element(log_level, file,
function, line,
2901 for (pIter = pcmk__first_xml_attr(
data); pIter != NULL; pIter = pIter->next) {
2902 const char *aname = (
const char*)pIter->name;
2904 p = pIter->_private;
2909 "%s %s @%s=%s",
flags, spaces, aname, value);
2921 flags = prefix_moved;
2927 "%s %s @%s=%s",
flags, spaces, aname, value);
2934 for (child = __xml_first_child(
data); child != NULL; child = __xml_next(child)) {
2935 __xml_log_change_element(log_level, file,
function, line, prefix, child, depth + 1, options);
2938 __xml_log_element(log_level, file,
function, line,
2942 for (child = __xml_first_child(
data); child != NULL; child = __xml_next(child)) {
2943 __xml_log_change_element(log_level, file,
function, line, prefix, child, depth + 1, options);
2953 const char *prefix, xmlNode *
data,
int depth,
int options)
2955 xmlNode *a_child = NULL;
2957 char *prefix_m = NULL;
2963 if (prefix == NULL) {
2970 "No data to dump as XML");
2975 __xml_log_change_element(log_level, file,
function, line, prefix,
data, depth, options);
2983 prefix_m = strdup(prefix);
2990 prefix_m = strdup(prefix);
2999 for (a_child = __xml_first_child(
data); a_child != NULL; a_child = __xml_next(a_child)) {
3000 log_data_element(log_level, file,
function, line, prefix, a_child, depth + 1, options);
3003 __xml_log_element(log_level, file,
function, line, prefix,
data, depth,
3010 dump_filtered_xml(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max)
3013 xmlAttrPtr xIter = NULL;
3014 static int filter_len =
DIMOF(filter);
3016 for (lpc = 0; options && lpc < filter_len; lpc++) {
3017 filter[lpc].found = FALSE;
3020 for (xIter = pcmk__first_xml_attr(
data); xIter != NULL; xIter = xIter->next) {
3022 const char *p_name = (
const char *)xIter->name;
3024 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
3025 if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].
string) == 0) {
3026 filter[lpc].found = TRUE;
3032 if (skip == FALSE) {
3033 dump_xml_attr(xIter, options, buffer, offset, max);
3039 dump_xml_element(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3041 const char *
name = NULL;
3052 if (*buffer == NULL) {
3060 insert_prefix(options, buffer, offset, max, depth);
3064 dump_filtered_xml(
data, options, buffer, offset, max);
3067 xmlAttrPtr xIter = NULL;
3069 for (xIter = pcmk__first_xml_attr(
data); xIter != NULL; xIter = xIter->next) {
3070 dump_xml_attr(xIter, options, buffer, offset, max);
3074 if (
data->children == NULL) {
3085 if (
data->children) {
3086 xmlNode *xChild = NULL;
3087 for(xChild =
data->children; xChild != NULL; xChild = xChild->next) {
3088 crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
3091 insert_prefix(options, buffer, offset, max, depth);
3101 dump_xml_text(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3112 if (*buffer == NULL) {
3117 insert_prefix(options, buffer, offset, max, depth);
3127 dump_xml_cdata(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3138 if (*buffer == NULL) {
3143 insert_prefix(options, buffer, offset, max, depth);
3156 dump_xml_comment(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3167 if (*buffer == NULL) {
3172 insert_prefix(options, buffer, offset, max, depth);
3183 #define PCMK__XMLDUMP_STATS 0
3203 #if (PCMK__XMLDUMP_STATS - 0)
3204 time_t next,
new = time(NULL);
3207 xmlOutputBuffer *xml_buffer;
3213 xml_buffer = xmlAllocOutputBuffer(NULL);
3226 xmlNodeDumpOutput(xml_buffer, doc,
data, 0,
3229 (void) xmlOutputBufferWrite(xml_buffer,
sizeof(
"\n") - 1,
"\n");
3230 if (xml_buffer->buffer != NULL) {
3232 (
char *) xmlBufContent(xml_buffer->buffer));
3235 #if (PCMK__XMLDUMP_STATS - 0)
3237 if ((now + 1) < next) {
3239 crm_err(
"xmlNodeDump() -> %dbytes took %ds", *max, next - now);
3244 (void) xmlOutputBufferClose(xml_buffer);
3248 switch(
data->type) {
3249 case XML_ELEMENT_NODE:
3251 dump_xml_element(
data, options, buffer, offset, max, depth);
3256 dump_xml_text(
data, options, buffer, offset, max, depth);
3259 case XML_COMMENT_NODE:
3260 dump_xml_comment(
data, options, buffer, offset, max, depth);
3262 case XML_CDATA_SECTION_NODE:
3263 dump_xml_cdata(
data, options, buffer, offset, max, depth);
3301 char *buffer = NULL;
3302 int offset = 0, max = 0;
3306 &buffer, &offset, &max, 0);
3313 char *buffer = NULL;
3314 int offset = 0, max = 0;
3323 char *buffer = NULL;
3324 int offset = 0, max = 0;
3326 crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
3333 if (xml_root != NULL && xml_root->children != NULL) {
3363 xmlNode *child = NULL;
3368 for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
3378 if (filename == NULL) {
3386 crm_info(
"Saving %s to %s", desc, filename);
3394 gboolean result = TRUE;
3395 int root_nodes_seen = 0;
3396 static struct qb_log_callsite *digest_cs = NULL;
3400 xmlNode *child_diff = NULL;
3402 xmlNode *removed =
find_xml_node(diff,
"diff-removed", FALSE);
3404 CRM_CHECK(new_xml != NULL,
return FALSE);
3405 if (digest_cs == NULL) {
3407 qb_log_callsite_get(__func__, __FILE__,
"diff-digest",
LOG_TRACE, __LINE__,
3412 for (child_diff = __xml_first_child(removed); child_diff != NULL;
3413 child_diff = __xml_next(child_diff)) {
3414 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3415 if (root_nodes_seen == 0) {
3421 if (root_nodes_seen == 0) {
3424 }
else if (root_nodes_seen > 1) {
3425 crm_err(
"(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3429 root_nodes_seen = 0;
3432 xmlNode *child_diff = NULL;
3434 for (child_diff = __xml_first_child(added); child_diff != NULL;
3435 child_diff = __xml_next(child_diff)) {
3436 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3437 if (root_nodes_seen == 0) {
3438 add_xml_object(NULL, *new_xml, child_diff, TRUE);
3444 if (root_nodes_seen > 1) {
3445 crm_err(
"(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3448 }
else if (result && digest) {
3449 char *new_digest = NULL;
3454 crm_info(
"Digest mis-match: expected %s, calculated %s", digest, new_digest);
3457 crm_trace(
"%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
3458 if (digest_cs && digest_cs->targets) {
3465 crm_trace(
"Digest matched: expected %s, calculated %s", digest, new_digest);
3469 }
else if (result) {
3486 for (xmlAttr *attr = pcmk__first_xml_attr(xml); attr; attr = attr->next) {
3501 mark_attr_deleted(xmlNode *new_xml,
const char *element,
const char *attr_name,
3502 const char *old_value)
3505 xmlAttr *attr = NULL;
3521 crm_trace(
"XML attribute %s=%s was removed from %s",
3522 attr_name, old_value, element);
3530 mark_attr_changed(xmlNode *new_xml,
const char *element,
const char *attr_name,
3531 const char *old_value)
3535 crm_trace(
"XML attribute %s was changed from '%s' to '%s' in %s",
3536 attr_name, old_value, vcopy, element);
3551 mark_attr_moved(xmlNode *new_xml,
const char *element, xmlAttr *old_attr,
3552 xmlAttr *new_attr,
int p_old,
int p_new)
3556 crm_trace(
"XML attribute %s moved from position %d to %d in %s",
3557 old_attr->name, p_old, p_new, element);
3560 __xml_node_dirty(new_xml);
3565 p = (p_old > p_new)? old_attr->_private : new_attr->_private;
3574 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
3576 xmlAttr *attr_iter = pcmk__first_xml_attr(old_xml);
3578 while (attr_iter != NULL) {
3579 xmlAttr *old_attr = attr_iter;
3580 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
3581 const char *
name = (
const char *) attr_iter->name;
3584 attr_iter = attr_iter->next;
3585 if (new_attr == NULL) {
3586 mark_attr_deleted(new_xml, (
const char *) old_xml->name,
name,
3591 int new_pos = __xml_offset((xmlNode*) new_attr);
3592 int old_pos = __xml_offset((xmlNode*) old_attr);
3598 if (strcmp(new_value, old_value) != 0) {
3599 mark_attr_changed(new_xml, (
const char *) old_xml->name,
name,
3602 }
else if ((old_pos != new_pos)
3604 mark_attr_moved(new_xml, (
const char *) old_xml->name,
3605 old_attr, new_attr, old_pos, new_pos);
3616 mark_created_attrs(xmlNode *new_xml)
3618 xmlAttr *attr_iter = pcmk__first_xml_attr(new_xml);
3620 while (attr_iter != NULL) {
3621 xmlAttr *new_attr = attr_iter;
3624 attr_iter = attr_iter->next;
3626 const char *attr_name = (
const char *) new_attr->name;
3628 crm_trace(
"Created new attribute %s=%s in %s",
3639 xmlUnsetProp(new_xml, new_attr->name);
3650 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
3653 xml_diff_old_attrs(old_xml, new_xml);
3654 mark_created_attrs(new_xml);
3667 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
3673 __xml_node_clean(candidate);
3679 free_xml_with_position(candidate, __xml_offset(old_child));
3681 if (find_element(new_parent, old_child, TRUE) == NULL) {
3687 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
3688 int p_old,
int p_new)
3692 crm_trace(
"Child element %s with id='%s' moved from position %d to %d under %s",
3693 new_child->name, (
ID(new_child)?
ID(new_child) :
"<no id>"),
3694 p_old, p_new, new_parent->name);
3695 __xml_node_dirty(new_parent);
3698 if (p_old > p_new) {
3699 p = old_child->_private;
3701 p = new_child->_private;
3707 __xml_diff_object(xmlNode *old_xml, xmlNode *new_xml,
bool check_top)
3709 xmlNode *cIter = NULL;
3713 if (old_xml == NULL) {
3714 crm_node_created(new_xml);
3719 p = new_xml->_private;
3728 xml_diff_attrs(old_xml, new_xml);
3731 for (cIter = __xml_first_child(old_xml); cIter != NULL; ) {
3732 xmlNode *old_child = cIter;
3733 xmlNode *new_child = find_element(new_xml, cIter, TRUE);
3735 cIter = __xml_next(cIter);
3737 __xml_diff_object(old_child, new_child, TRUE);
3740 mark_child_deleted(old_child, new_xml);
3745 for (cIter = __xml_first_child(new_xml); cIter != NULL; ) {
3746 xmlNode *new_child = cIter;
3747 xmlNode *old_child = find_element(old_xml, cIter, TRUE);
3749 cIter = __xml_next(cIter);
3750 if(old_child == NULL) {
3752 p = new_child->_private;
3754 __xml_diff_object(old_child, new_child, TRUE);
3758 int p_new = __xml_offset(new_child);
3759 int p_old = __xml_offset(old_child);
3761 if(p_old != p_new) {
3762 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
3779 crm_element_name(new_xml)),
3787 __xml_diff_object(old_xml, new_xml, FALSE);
3793 xmlNode *tmp1 = NULL;
3810 if (added->children == NULL && removed->children == NULL) {
3821 xmlNode *cIter = NULL;
3822 xmlAttrPtr pIter = NULL;
3823 gboolean can_prune = TRUE;
3824 const char *
name = crm_element_name(xml_node);
3833 for (pIter = pcmk__first_xml_attr(xml_node); pIter != NULL; pIter = pIter->next) {
3834 const char *p_name = (
const char *)pIter->name;
3842 cIter = __xml_first_child(xml_node);
3844 xmlNode *child = cIter;
3846 cIter = __xml_next(cIter);
3857 find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact)
3859 xmlNode *a_child = NULL;
3860 int search_offset = __xml_offset(search_comment);
3862 CRM_CHECK(search_comment->type == XML_COMMENT_NODE,
return NULL);
3864 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
3866 int offset = __xml_offset(a_child);
3869 if (offset < search_offset) {
3872 }
else if (offset > search_offset) {
3881 if (a_child->type == XML_COMMENT_NODE
3882 &&
safe_str_eq((
const char *)a_child->content, (
const char *)search_comment->content)) {
3894 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
3898 CRM_CHECK(left->type == XML_COMMENT_NODE,
return NULL);
3901 ||
safe_str_neq((
const char *)left->content, (
const char *)right->content)) {
3902 xmlNode *deleted = NULL;
3915 gboolean full, gboolean * changed,
const char *marker)
3917 gboolean dummy = FALSE;
3918 gboolean skip = FALSE;
3919 xmlNode *diff = NULL;
3920 xmlNode *right_child = NULL;
3921 xmlNode *left_child = NULL;
3922 xmlAttrPtr xIter = NULL;
3924 const char *
id = NULL;
3925 const char *
name = NULL;
3926 const char *value = NULL;
3927 const char *right_val = NULL;
3930 static int filter_len =
DIMOF(filter);
3932 if (changed == NULL) {
3940 if (left->type == XML_COMMENT_NODE) {
3941 return subtract_xml_comment(parent, left, right, changed);
3945 if (right == NULL) {
3946 xmlNode *deleted = NULL;
3948 crm_trace(
"Processing <%s id=%s> (complete copy)", crm_element_name(left),
id);
3956 name = crm_element_name(left);
3962 if (value != NULL && strcmp(value,
"removed:top") == 0) {
3963 crm_trace(
"We are the root of the deletion: %s.id=%s",
name,
id);
3972 for (lpc = 0; lpc < filter_len; lpc++) {
3973 filter[lpc].found = FALSE;
3977 for (left_child = __xml_first_child(left); left_child != NULL;
3978 left_child = __xml_next(left_child)) {
3979 gboolean child_changed = FALSE;
3981 right_child = find_element(right, left_child, FALSE);
3983 if (child_changed) {
3988 if (*changed == FALSE) {
3992 xmlAttrPtr pIter = NULL;
3994 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3995 const char *p_name = (
const char *)pIter->name;
3996 const char *p_value = pcmk__xml_attr_value(pIter);
4006 for (xIter = pcmk__first_xml_attr(left); xIter != NULL; xIter = xIter->next) {
4007 const char *prop_name = (
const char *)xIter->name;
4008 xmlAttrPtr right_attr = NULL;
4018 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
4019 if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].
string) == 0) {
4020 filter[lpc].found = TRUE;
4030 right_attr = xmlHasProp(right, (
pcmkXmlStr) prop_name);
4032 p = right_attr->_private;
4040 xmlAttrPtr pIter = NULL;
4042 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
4043 const char *p_name = (
const char *)pIter->name;
4044 const char *p_value = pcmk__xml_attr_value(pIter);
4061 if (strcmp(left_value, right_val) == 0) {
4067 xmlAttrPtr pIter = NULL;
4069 crm_trace(
"Changes detected to %s in <%s id=%s>", prop_name,
4070 crm_element_name(left),
id);
4071 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
4072 const char *p_name = (
const char *)pIter->name;
4073 const char *p_value = pcmk__xml_attr_value(pIter);
4080 crm_trace(
"Changes detected to %s (%s -> %s) in <%s id=%s>",
4081 prop_name, left_value, right_val, crm_element_name(left),
id);
4088 if (*changed == FALSE) {
4092 }
else if (full == FALSE &&
id) {
4100 add_xml_comment(xmlNode * parent, xmlNode *
target, xmlNode * update)
4103 CRM_CHECK(update->type == XML_COMMENT_NODE,
return 0);
4106 target = find_xml_comment(parent, update, FALSE);
4113 }
else if (
safe_str_neq((
const char *)
target->content, (
const char *)update->content)) {
4114 xmlFree(
target->content);
4115 target->content = xmlStrdup(update->content);
4122 add_xml_object(xmlNode * parent, xmlNode *
target, xmlNode * update, gboolean as_diff)
4124 xmlNode *a_child = NULL;
4125 const char *object_name = NULL,
4126 *object_href = NULL,
4127 *object_href_val = NULL;
4136 if (update->type == XML_COMMENT_NODE) {
4137 return add_xml_comment(parent,
target, update);
4140 object_name = crm_element_name(update);
4141 object_href_val =
ID(update);
4142 if (object_href_val != NULL) {
4149 CRM_CHECK(object_name != NULL,
return 0);
4153 target = find_entity_by_attr_or_just_name(parent, object_name,
4154 object_href, object_href_val);
4160 #if XML_PARSER_DEBUG
4162 object_href ?
" " :
"",
4163 object_href ? object_href :
"",
4164 object_href ?
"=" :
"",
4165 object_href ? object_href_val :
"");
4169 object_href ?
" " :
"",
4170 object_href ? object_href :
"",
4171 object_href ?
"=" :
"",
4172 object_href ? object_href_val :
"");
4178 if (as_diff == FALSE) {
4184 xmlAttrPtr pIter = NULL;
4186 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
4187 const char *p_name = (
const char *)pIter->name;
4188 const char *p_value = pcmk__xml_attr_value(pIter);
4196 for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
4197 #if XML_PARSER_DEBUG
4199 object_href ?
" " :
"",
4200 object_href ? object_href :
"",
4201 object_href ?
"=" :
"",
4202 object_href ? object_href_val :
"");
4204 add_xml_object(
target, NULL, a_child, as_diff);
4207 #if XML_PARSER_DEBUG
4209 object_href ?
" " :
"",
4210 object_href ? object_href :
"",
4211 object_href ?
"=" :
"",
4212 object_href ? object_href_val :
"");
4220 gboolean can_update = TRUE;
4221 xmlNode *child_of_child = NULL;
4224 CRM_CHECK(to_update != NULL,
return FALSE);
4226 if (
safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
4232 }
else if (can_update) {
4233 #if XML_PARSER_DEBUG
4236 add_xml_object(NULL, child, to_update, FALSE);
4239 for (child_of_child = __xml_first_child(child); child_of_child != NULL;
4240 child_of_child = __xml_next(child_of_child)) {
4253 const char *tag,
const char *field,
const char *value, gboolean search_matches)
4255 int match_found = 0;
4258 CRM_CHECK(children != NULL,
return FALSE);
4260 if (tag != NULL &&
safe_str_neq(tag, crm_element_name(root))) {
4265 if (*children == NULL) {
4272 if (search_matches || match_found == 0) {
4273 xmlNode *child = NULL;
4275 for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
4276 match_found +=
find_xml_children(children, child, tag, field, value, search_matches);
4286 gboolean can_delete = FALSE;
4287 xmlNode *child_of_child = NULL;
4289 const char *up_id = NULL;
4290 const char *child_id = NULL;
4291 const char *right_val = NULL;
4294 CRM_CHECK(update != NULL,
return FALSE);
4297 child_id =
ID(child);
4299 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
4302 if (
safe_str_neq(crm_element_name(update), crm_element_name(child))) {
4305 if (can_delete && delete_only) {
4306 xmlAttrPtr pIter = NULL;
4308 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
4309 const char *p_name = (
const char *)pIter->name;
4310 const char *p_value = pcmk__xml_attr_value(pIter);
4319 if (can_delete && parent != NULL) {
4321 if (delete_only || update == NULL) {
4326 xmlDoc *doc = tmp->doc;
4327 xmlNode *old = NULL;
4330 old = xmlReplaceNode(child, tmp);
4338 xmlDocSetRootElement(doc, old);
4344 }
else if (can_delete) {
4349 child_of_child = __xml_first_child(child);
4350 while (child_of_child) {
4351 xmlNode *next = __xml_next(child_of_child);
4357 child_of_child = NULL;
4359 child_of_child = next;
4369 xmlNode *child = NULL;
4370 GSList *nvpairs = NULL;
4371 xmlNode *result = NULL;
4372 const char *
name = NULL;
4376 name = crm_element_name(input);
4385 for (child = __xml_first_child(input); child != NULL;
4386 child = __xml_next(child)) {
4401 xmlNode *match = NULL;
4403 for (match = __xml_first_child_element(parent); match != NULL;
4404 match = __xml_next_element(match)) {
4410 if (
name == NULL || strcmp((
const char *)match->name,
name) == 0) {
4427 xmlNode *match = __xml_next_element(sibling);
4428 const char *
name = crm_element_name(sibling);
4430 while (match != NULL) {
4431 if (!strcmp(crm_element_name(match),
name)) {
4434 match = __xml_next_element(match);
4442 static bool init = TRUE;
4451 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
4454 xmlDeregisterNodeDefault(pcmkDeregisterNode);
4455 xmlRegisterNodeDefault(pcmkRegisterNode);
4464 crm_info(
"Cleaning up memory from libxml2");
4469 #define XPATH_MAX 512
4474 const char *tag = NULL;
4475 const char *ref = NULL;
4476 xmlNode *result = input;
4478 if (result == NULL) {
4481 }
else if (top == NULL) {
4485 tag = crm_element_name(result);
4492 if (result == NULL) {
4493 char *nodePath = (
char *)xmlGetNodePath(top);
4495 crm_err(
"No match for %s found in %s: Invalid configuration", xpath_string,
4513 static const char *base = NULL;
4517 base = getenv(
"PCMK_schema_directory");
4519 if (pcmk__str_empty(base)) {
4533 crm_err(
"XML artefact family specified as %u not recognized", ns);
4553 crm_err(
"XML artefact family specified as %u not recognized", ns);
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
void pcmk__free_acls(GList *acls)
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
void pcmk__apply_acl(xmlNode *xml)
void xml_acl_disable(xmlNode *xml)
bool pcmk__ends_with_ext(const char *s, const char *match)
void crm_schema_cleanup(void)
const char * pcmk__get_tmpdir(void)
void crm_schema_init(void)
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
gboolean safe_str_neq(const char *a, const char *b)
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
int char2score(const char *score)
int compare_version(const char *version1, const char *version2)
char * crm_generate_uuid(void)
#define safe_str_eq(a, b)
#define CRM_SCHEMA_DIRECTORY
#define clear_bit(word, bit)
#define set_bit(word, bit)
const char * pcmk__epoch2str(time_t *when)
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
#define crm_log_xml_info(xml, text)
#define crm_info(fmt, args...)
#define crm_warn(fmt, args...)
#define crm_log_xml_debug(xml, text)
#define CRM_LOG_ASSERT(expr)
unsigned int crm_trace_nonlog
#define crm_log_xml_explicit(xml, text)
#define crm_log_xml_err(xml, text)
#define crm_notice(fmt, args...)
@ xml_log_option_diff_minus
@ xml_log_option_filtered
@ xml_log_option_diff_all
@ xml_log_option_diff_short
@ xml_log_option_diff_plus
@ xml_log_option_dirty_add
@ xml_log_option_full_fledged
@ xml_log_option_formatted
@ xml_log_option_children
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
#define CRM_CHECK(expr, failure_action)
#define crm_debug(fmt, args...)
#define crm_err(fmt, args...)
#define do_crm_log_alias(level, file, function, line, fmt, args...)
Log a message as if it came from a different code location.
#define crm_log_xml_trace(xml, text)
#define crm_log_xml_warn(xml, text)
#define crm_trace(fmt, args...)
#define XML_TAG_RESOURCE_REF
#define XML_ATTR_CRM_VERSION
#define XML_ACL_TAG_ROLE_REF
#define XML_NVPAIR_ATTR_VALUE
#define XML_CIB_TAG_OBJ_REF
#define XML_DIFF_POSITION
#define XML_ATTR_UPDATE_CLIENT
#define XML_CIB_TAG_CONFIGURATION
#define XML_NVPAIR_ATTR_NAME
#define XML_ATTR_GENERATION_ADMIN
#define XML_ATTR_NUMUPDATES
#define XML_CIB_ATTR_WRITTEN
#define XML_ACL_TAG_ROLE_REFv1
#define XML_ATTR_UPDATE_ORIG
#define XML_ATTR_UPDATE_USER
#define XML_ATTR_GENERATION
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node's attributes.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
#define pcmk_err_old_data
const char * bz2_strerror(int rc)
#define pcmk_err_diff_failed
#define pcmk_err_diff_resync
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
char * dump_xml_unformatted(xmlNode *an_xml_node)
gboolean xml_has_children(const xmlNode *xml_root)
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
int get_tag_name(const char *input, size_t offset, size_t max)
xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
void pcmk__mark_xml_attr_dirty(xmlAttr *a)
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, gboolean *changed, const char *marker)
struct xml_deleted_obj_s xml_deleted_obj_t
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
void fix_plus_plus_recursive(xmlNode *target)
xmlNode * find_xml_node(xmlNode *root, const char *search_path, gboolean must_find)
xmlNode * copy_xml(xmlNode *src)
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
void xml_accept_changes(xmlNode *xml)
struct xml_change_obj_s xml_change_obj_t
void xml_log_patchset(uint8_t log_level, const char *function, xmlNode *patchset)
void crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
xmlNode * stdin2xml(void)
void pcmk__set_xml_flag(xmlNode *xml, enum xml_private_flags flag)
#define buffer_print(buffer, max, offset, fmt, args...)
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
xmlNode * get_message_xml(xmlNode *msg, const char *field)
void strip_text_nodes(xmlNode *xml)
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
#define PCMK__XML_PARSE_OPTS
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, int options)
xmlNode * filename2xml(const char *filename)
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
char * dump_xml_formatted(xmlNode *an_xml_node)
void purge_diff_markers(xmlNode *a_node)
bool xml_tracking_changes(xmlNode *xml)
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
void crm_xml_cleanup(void)
bool xml_document_dirty(xmlNode *xml)
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
void crm_xml_dump(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
void free_xml(xmlNode *child)
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
int get_attr_value(const char *input, size_t offset, size_t max)
void crm_destroy_xml(gpointer data)
xmlNode destructor which can be used in glib collections
int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
char * crm_xml_escape(const char *text)
int get_attr_name(const char *input, size_t offset, size_t max)
#define XML_PRIVATE_MAGIC
void xml_log_changes(uint8_t log_level, const char *function, xmlNode *xml)
xmlDoc * getDocPtr(xmlNode *node)
xmlNode * create_xml_node(xmlNode *parent, const char *name)
void copy_in_properties(xmlNode *target, xmlNode *src)
xmlNode * first_named_child(const xmlNode *parent, const char *name)
xmlNode * string2xml(const char *input)
const char * crm_xml_add_last_written(xmlNode *xml_node)
gboolean can_prune_leaf(xmlNode *xml_node)
void pcmk_free_xml_subtree(xmlNode *xml)
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
void xml_remove_prop(xmlNode *obj, const char *name)
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
char * xml_get_path(xmlNode *xml)
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Wrappers for and extensions to libxml2.
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
const xmlChar * pcmkXmlStr
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
@ pcmk__xml_artefact_ns_legacy_xslt
@ pcmk__xml_artefact_ns_legacy_rng
@ pcmk__xml_artefact_ns_base_rng
@ pcmk__xml_artefact_ns_base_xslt
#define CRM_XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.