22 #include <sys/types.h> 40 # include <libxml/parser.h> 41 # include <libxml/tree.h> 44 #define XML_BUFFER_SIZE 4096 45 #define XML_PARSER_DEBUG 0 56 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER) 63 typedef struct xml_deleted_obj_s {
70 static filter_t filter[] = {
79 static xmlNode *subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right, gboolean * changed);
80 static xmlNode *find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact);
81 static int add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update);
83 #define CHUNK_SIZE 1024 88 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
92 }
else if (lazy && is_not_set(((
xml_private_t *)xml->doc->_private)->flags,
99 #define buffer_print(buffer, max, offset, fmt, args...) do { \ 102 rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \ 104 if(buffer && rc < 0) { \ 105 crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \ 106 (buffer)[(offset)] = 0; \ 108 } else if(rc >= ((max) - (offset))) { \ 110 (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \ 111 tmp = realloc_safe((buffer), (max)); \ 121 insert_prefix(
int options,
char **buffer,
int *offset,
int *max,
int depth)
124 size_t spaces = 2 * depth;
126 if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
128 (*buffer) = realloc_safe((*buffer), (*max));
130 memset((*buffer) + (*offset),
' ', spaces);
136 set_parent_flag(xmlNode *xml,
long flag)
139 for(; xml; xml = xml->parent) {
155 if(xml && xml->doc && xml->doc->_private){
165 __xml_node_dirty(xmlNode *xml)
172 __xml_node_clean(xmlNode *xml)
174 xmlNode *cIter = NULL;
181 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
182 __xml_node_clean(cIter);
187 crm_node_created(xmlNode *xml)
189 xmlNode *cIter = NULL;
195 __xml_node_dirty(xml);
198 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
199 crm_node_created(cIter);
207 xmlNode *parent = a->parent;
216 __xml_node_dirty(parent);
219 int get_tag_name(
const char *input,
size_t offset,
size_t max);
220 int get_attr_name(
const char *input,
size_t offset,
size_t max);
225 xmlNode * xml_node, xmlNode * parent);
227 int add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff);
229 #define XML_PRIVATE_MAGIC (long) 0x81726354 232 __xml_deleted_obj_free(
void *
data)
237 free(deleted_obj->path);
257 g_list_free_full(p->
deleted_objs, __xml_deleted_obj_free);
267 __xml_private_clean(p);
272 pcmkDeregisterNode(xmlNodePtr node)
274 __xml_private_free(node->_private);
278 pcmkRegisterNode(xmlNodePtr node)
283 case XML_ELEMENT_NODE:
284 case XML_DOCUMENT_NODE:
285 case XML_ATTRIBUTE_NODE:
286 case XML_COMMENT_NODE:
295 case XML_CDATA_SECTION_NODE:
299 crm_trace(
"Ignoring %p %d", node, node->type);
309 __xml_node_dirty(node);
317 crm_trace(
"Tracking changes%s to %p", enforce_acls?
" with ACLs":
"", xml);
320 if(acl_source == NULL) {
342 if(xml != NULL && xml->doc && xml->doc->_private) {
398 static int __xml_offset(xmlNode *xml)
401 xmlNode *cIter = NULL;
403 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
414 static int __xml_offset_no_deletions(xmlNode *xml)
417 xmlNode *cIter = NULL;
419 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
431 __xml_build_changes(xmlNode * xml, xmlNode *patchset)
433 xmlNode *cIter = NULL;
434 xmlAttr *pIter = NULL;
435 xmlNode *change = NULL;
443 sizeof(buffer)) > 0) {
444 int position = __xml_offset_no_deletions(xml);
457 for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
458 xmlNode *attr = NULL;
470 sizeof(buffer)) > 0) {
495 xmlNode *result = NULL;
500 for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
505 crm_xml_add(result, (
const char *)pIter->name, value);
510 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
511 __xml_build_changes(cIter, patchset);
519 crm_trace(
"%s.%s moved to position %d", xml->name,
ID(xml), __xml_offset(xml));
521 sizeof(buffer)) > 0) {
532 __xml_accept_changes(xmlNode * xml)
534 xmlNode *cIter = NULL;
535 xmlAttr *pIter = NULL;
539 pIter = pcmk__first_xml_attr(xml);
541 while (pIter != NULL) {
542 const xmlChar *name = pIter->name;
555 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
556 __xml_accept_changes(cIter);
561 is_config_change(xmlNode *xml)
568 p = config->_private;
574 if(xml->doc && xml->doc->_private) {
575 p = xml->doc->_private;
576 for(gIter = p->
deleted_objs; gIter; gIter = gIter->next) {
589 xml_repair_v1_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
593 xmlNode *diff_child = NULL;
595 const char *tag = NULL;
597 const char *vfields[] = {
603 if (local_diff == NULL) {
608 tag =
"diff-removed";
610 if (diff_child == NULL) {
620 for(lpc = 0; last && lpc <
DIMOF(vfields); lpc++){
624 if(changed || lpc == 2) {
631 if (diff_child == NULL) {
641 for(lpc = 0; next && lpc <
DIMOF(vfields); lpc++){
648 xmlAttrPtr xIter = NULL;
650 for (xIter = next->properties; xIter; xIter = xIter->next) {
651 const char *p_name = (
const char *)xIter->name;
654 xmlSetProp(cib, (
const xmlChar *)p_name, (
const xmlChar *)p_value);
662 xml_create_patchset_v1(xmlNode *source, xmlNode *target,
bool config,
bool suppress)
668 xml_repair_v1_diff(source, target, patchset, config);
675 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
683 xmlNode *patchset = NULL;
684 const char *vfields[] = {
696 doc = target->doc->_private;
704 for(lpc = 0; lpc <
DIMOF(vfields); lpc++){
714 for(lpc = 0; lpc <
DIMOF(vfields); lpc++){
723 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
729 if (deleted_obj->position >= 0) {
734 __xml_build_changes(target, patchset);
738 static gboolean patch_legacy_mode(
void)
740 static gboolean init = TRUE;
741 static gboolean legacy = FALSE;
754 xml_create_patchset(
int format, xmlNode *source, xmlNode *target,
bool *config_changed,
bool manage_version)
758 xmlNode *patch = NULL;
767 config = is_config_change(target);
769 *config_changed = config;
772 if(manage_version && config) {
779 }
else if(manage_version) {
786 if(patch_legacy_mode()) {
795 crm_trace(
"Using patch format %d for version: %s", format, version);
800 patch = xml_create_patchset_v1(source, target, config, FALSE);
803 patch = xml_create_patchset_v2(source, target);
806 crm_err(
"Unknown patch format: %d", format);
820 if (patch == NULL || source == NULL || target == NULL) {
829 if (format > 1 && with_digest == FALSE) {
843 __xml_log_element(
int log_level,
const char *file,
const char *
function,
int line,
844 const char *prefix, xmlNode *
data,
int depth,
int options);
850 xmlNode *child = NULL;
851 xmlNode *added = NULL;
852 xmlNode *removed = NULL;
853 gboolean is_first = TRUE;
855 int add[] = { 0, 0, 0 };
856 int del[] = { 0, 0, 0 };
858 const char *fmt = NULL;
859 const char *digest = NULL;
862 static struct qb_log_callsite *patchset_cs = NULL;
864 if (patchset_cs == NULL) {
865 patchset_cs = qb_log_callsite_get(
function, __FILE__,
"xml-patchset", log_level, __LINE__, 0);
868 if (patchset == NULL) {
872 }
else if (log_level == 0) {
882 if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
884 "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
886 "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
888 }
else if (patchset != NULL && (add[0] || add[1] || add[2])) {
890 "%s: Local-only Change: %d.%d.%d",
function ?
function :
"",
891 add[0], add[1], add[2]);
896 xmlNode *change = NULL;
898 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
903 }
else if(strcmp(op,
"create") == 0) {
904 int lpc = 0, max = 0;
907 max = strlen(prefix);
908 __xml_log_element(log_level, __FILE__,
function, __LINE__, prefix, change->children,
911 for(lpc = 2; lpc < max; lpc++) {
915 __xml_log_element(log_level, __FILE__,
function, __LINE__, prefix, change->children,
919 }
else if(strcmp(op,
"move") == 0) {
922 }
else if(strcmp(op,
"modify") == 0) {
931 for (child = __xml_first_child(clist); child != NULL; child = __xml_next(child)) {
936 }
else if(strcmp(op,
"set") == 0) {
942 o_set += snprintf(buffer_set + o_set,
XML_BUFFER_SIZE - o_set,
"@%s=%s", name, value);
944 }
else if(strcmp(op,
"unset") == 0) {
946 o_unset += snprintf(buffer_unset + o_unset,
XML_BUFFER_SIZE - o_unset,
", ");
948 o_unset += snprintf(buffer_unset + o_unset,
XML_BUFFER_SIZE - o_unset,
"@%s", name);
952 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"+ %s: %s", xpath, buffer_set);
955 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"-- %s: %s", xpath, buffer_unset);
958 }
else if(strcmp(op,
"delete") == 0) {
963 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"-- %s (%d)", xpath, position);
973 if (log_level < LOG_DEBUG ||
function == NULL) {
978 for (child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
990 for (child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
1010 doc = xml->doc->_private;
1015 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
1018 if (deleted_obj->position >= 0) {
1020 deleted_obj->path, deleted_obj->position);
1035 xmlNode *top = NULL;
1042 crm_trace(
"Accepting changes to %p", xml);
1043 doc = xml->doc->_private;
1044 top = xmlDocGetRootElement(xml->doc);
1046 __xml_private_clean(xml->doc->_private);
1054 __xml_accept_changes(top);
1058 find_element(xmlNode *haystack, xmlNode *needle, gboolean exact)
1061 return (needle->type == XML_COMMENT_NODE)?
1062 find_xml_comment(haystack, needle, exact)
1063 :
find_entity(haystack, crm_element_name(needle),
ID(needle));
1068 __subtract_xml_object(xmlNode * target, xmlNode * patch)
1070 xmlNode *patch_child = NULL;
1071 xmlNode *cIter = NULL;
1072 xmlAttrPtr xIter = NULL;
1075 const char *name = NULL;
1076 const char *value = NULL;
1078 if (target == NULL || patch == NULL) {
1082 if (target->type == XML_COMMENT_NODE) {
1085 subtract_xml_comment(target->parent, target, patch, &dummy);
1088 name = crm_element_name(target);
1096 if (value != NULL && strcmp(value,
"removed:top") == 0) {
1097 crm_trace(
"We are the root of the deletion: %s.id=%s", name,
id);
1103 for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1104 const char *p_name = (
const char *)xIter->name;
1113 cIter = __xml_first_child(target);
1115 xmlNode *target_child = cIter;
1117 cIter = __xml_next(cIter);
1118 patch_child = find_element(patch, target_child, FALSE);
1119 __subtract_xml_object(target_child, patch_child);
1125 __add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * patch)
1127 xmlNode *patch_child = NULL;
1128 xmlNode *target_child = NULL;
1129 xmlAttrPtr xIter = NULL;
1131 const char *
id = NULL;
1132 const char *name = NULL;
1133 const char *value = NULL;
1135 if (patch == NULL) {
1137 }
else if (parent == NULL && target == NULL) {
1145 && strcmp(value,
"added:top") == 0) {
1147 name = crm_element_name(patch);
1148 crm_trace(
"We are the root of the addition: %s.id=%s", name,
id);
1152 }
else if(target == NULL) {
1154 name = crm_element_name(patch);
1155 crm_err(
"Could not locate: %s.id=%s", name,
id);
1159 if (target->type == XML_COMMENT_NODE) {
1160 add_xml_comment(parent, target, patch);
1163 name = crm_element_name(target);
1168 for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1169 const char *p_name = (
const char *)xIter->name;
1177 for (patch_child = __xml_first_child(patch); patch_child != NULL;
1178 patch_child = __xml_next(patch_child)) {
1180 target_child = find_element(target, patch_child, FALSE);
1181 __add_xml_object(target, target_child, patch_child);
1197 find_patch_xml_node(xmlNode *patchset,
int format,
bool added,
1198 xmlNode **patch_node)
1205 label = added?
"diff-added" :
"diff-removed";
1208 if (cib_node != NULL) {
1209 *patch_node = cib_node;
1213 label = added?
"target" :
"source";
1218 crm_warn(
"Unknown patch format: %d", format);
1229 xmlNode *tmp = NULL;
1231 const char *vfields[] = {
1241 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
1245 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1247 crm_trace(
"Got %d for del[%s]", del[lpc], vfields[lpc]);
1252 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
1256 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1258 crm_trace(
"Got %d for add[%s]", add[lpc], vfields[lpc]);
1266 xml_patch_version_check(xmlNode *xml, xmlNode *patchset,
int format)
1269 bool changed = FALSE;
1271 int this[] = { 0, 0, 0 };
1272 int add[] = { 0, 0, 0 };
1273 int del[] = { 0, 0, 0 };
1275 const char *vfields[] = {
1281 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1283 crm_trace(
"Got %d for this[%s]",
this[lpc], vfields[lpc]);
1284 if (
this[lpc] < 0) {
1292 add[2] =
this[2] + 1;
1293 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1294 del[lpc] =
this[lpc];
1299 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1300 if(
this[lpc] < del[lpc]) {
1301 crm_debug(
"Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)", vfields[lpc],
1302 this[0],
this[1],
this[2], del[0], del[1], del[2], add[0], add[1], add[2]);
1305 }
else if(
this[lpc] > del[lpc]) {
1306 crm_info(
"Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p", vfields[lpc],
1307 this[0],
this[1],
this[2], del[0], del[1], del[2], add[0], add[1], add[2], patchset);
1313 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1314 if(add[lpc] > del[lpc]) {
1319 if(changed == FALSE) {
1320 crm_notice(
"Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
1324 crm_debug(
"Can apply patch %d.%d.%d to %d.%d.%d",
1325 add[0], add[1], add[2],
this[0],
this[1],
this[2]);
1330 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset)
1333 int root_nodes_seen = 0;
1335 xmlNode *child_diff = NULL;
1336 xmlNode *added =
find_xml_node(patchset,
"diff-added", FALSE);
1337 xmlNode *removed =
find_xml_node(patchset,
"diff-removed", FALSE);
1341 for (child_diff = __xml_first_child(removed); child_diff != NULL;
1342 child_diff = __xml_next(child_diff)) {
1343 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1344 if (root_nodes_seen == 0) {
1345 __subtract_xml_object(xml, child_diff);
1350 if (root_nodes_seen > 1) {
1351 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1355 root_nodes_seen = 0;
1358 xmlNode *child_diff = NULL;
1360 for (child_diff = __xml_first_child(added); child_diff != NULL;
1361 child_diff = __xml_next(child_diff)) {
1362 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1363 if (root_nodes_seen == 0) {
1364 __add_xml_object(NULL, xml, child_diff);
1370 if (root_nodes_seen > 1) {
1371 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1382 __first_xml_child_match(xmlNode *parent,
const char *name,
const char *
id,
int position)
1384 xmlNode *cIter = NULL;
1386 for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
1387 if(strcmp((
const char *)cIter->name, name) != 0) {
1390 const char *cid =
ID(cIter);
1391 if(cid == NULL || strcmp(cid,
id) != 0) {
1397 if (cIter->type == XML_COMMENT_NODE
1399 && __xml_offset(cIter) != position) {
1409 __xml_find_path(xmlNode *top,
const char *key,
int target_position)
1411 xmlNode *target = (xmlNode*)top->doc;
1415 char *current = strdup(key);
1420 rc = sscanf (current,
"/%[^/]%s", section, remainder);
1430 }
else if(tag && section) {
1431 int f = sscanf (section,
"%[^[][@id='%[^']", tag,
id);
1432 int current_position = -1;
1435 if (rc == 1 && target_position >= 0) {
1436 current_position = target_position;
1441 target = __first_xml_child_match(target, tag, NULL, current_position);
1444 target = __first_xml_child_match(target, tag,
id, current_position);
1452 if(rc == 1 || target == NULL) {
1457 char *tmp = current;
1458 current = remainder;
1465 char *path = (
char *)xmlGetNodePath(target);
1467 crm_trace(
"Found %s for %s", path, key);
1481 typedef struct xml_change_obj_s {
1487 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
1491 int position_a = -1;
1492 int position_b = -1;
1497 if (position_a < position_b) {
1500 }
else if (position_a > position_b) {
1508 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
1511 xmlNode *change = NULL;
1515 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
1516 xmlNode *match = NULL;
1525 crm_trace(
"Processing %s %s", change->name, op);
1528 if(strcmp(op,
"delete") == 0) {
1534 match = __xml_find_path(xml, xpath, position);
1536 crm_trace(
"Performing %s on %s with %p", op, xpath, match);
1538 if(match == NULL && strcmp(op,
"delete") == 0) {
1539 crm_debug(
"No %s match for %s in %p", op, xpath, xml->doc);
1542 }
else if(match == NULL) {
1543 crm_err(
"No %s match for %s in %p", op, xpath, xml->doc);
1547 }
else if (strcmp(op,
"create") == 0 || strcmp(op,
"move") == 0) {
1553 change_obj->change = change;
1554 change_obj->match = match;
1556 change_objs = g_list_append(change_objs, change_obj);
1558 if (strcmp(op,
"move") == 0) {
1560 if (match->parent != NULL && match->parent->last != NULL) {
1561 xmlAddNextSibling(match->parent->last, match);
1565 }
else if(strcmp(op,
"delete") == 0) {
1568 }
else if(strcmp(op,
"modify") == 0) {
1569 xmlAttr *pIter = pcmk__first_xml_attr(match);
1576 while(pIter != NULL) {
1577 const char *name = (
const char *)pIter->name;
1579 pIter = pIter->next;
1583 for (pIter = pcmk__first_xml_attr(attrs); pIter != NULL; pIter = pIter->next) {
1584 const char *name = (
const char *)pIter->name;
1591 crm_err(
"Unknown operation: %s", op);
1597 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
1599 for (gIter = change_objs; gIter; gIter = gIter->next) {
1601 xmlNode *match = change_obj->match;
1602 const char *op = NULL;
1603 const char *xpath = NULL;
1605 change = change_obj->change;
1610 crm_trace(
"Continue performing %s on %s with %p", op, xpath, match);
1612 if(strcmp(op,
"create") == 0) {
1614 xmlNode *child = NULL;
1615 xmlNode *match_child = NULL;
1617 match_child = match->children;
1620 while(match_child && position != __xml_offset(match_child)) {
1621 match_child = match_child->next;
1624 child = xmlDocCopyNode(change->children, match->doc, 1);
1626 crm_trace(
"Adding %s at position %d", child->name, position);
1627 xmlAddPrevSibling(match_child, child);
1629 }
else if(match->last) {
1630 crm_trace(
"Adding %s at position %d (end)", child->name, position);
1631 xmlAddNextSibling(match->last, child);
1634 crm_trace(
"Adding %s at position %d (first)", child->name, position);
1636 xmlAddChild(match, child);
1638 crm_node_created(child);
1640 }
else if(strcmp(op,
"move") == 0) {
1644 if(position != __xml_offset(match)) {
1645 xmlNode *match_child = NULL;
1648 if(p > __xml_offset(match)) {
1653 match_child = match->parent->children;
1655 while(match_child && p != __xml_offset(match_child)) {
1656 match_child = match_child->next;
1659 crm_trace(
"Moving %s to position %d (was %d, prev %p, %s %p)",
1660 match->name, position, __xml_offset(match), match->prev,
1661 match_child?
"next":
"last", match_child?match_child:match->parent->last);
1664 xmlAddPrevSibling(match_child, match);
1668 xmlAddNextSibling(match->parent->last, match);
1672 crm_trace(
"%s is already in position %d", match->name, position);
1675 if(position != __xml_offset(match)) {
1676 crm_err(
"Moved %s.%d to position %d instead of %d (%p)",
1677 match->name,
ID(match), __xml_offset(match), position, match->prev);
1683 g_list_free_full(change_objs, free);
1692 xmlNode *old = NULL;
1695 if(patchset == NULL) {
1703 rc = xml_patch_version_check(xml, patchset, format);
1717 rc = xml_apply_patchset_v1(xml, patchset);
1720 rc = xml_apply_patchset_v2(xml, patchset);
1723 crm_err(
"Unknown patch format: %d", format);
1729 static struct qb_log_callsite *digest_cs = NULL;
1731 char *new_digest = NULL;
1734 if (digest_cs == NULL) {
1736 qb_log_callsite_get(__func__, __FILE__,
"diff-digest",
LOG_TRACE, __LINE__,
1742 crm_info(
"v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
1745 if (digest_cs && digest_cs->targets) {
1751 crm_trace(
"%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
1755 crm_trace(
"v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
1767 xmlNode *a_child = NULL;
1768 const char *name =
"NULL";
1771 name = crm_element_name(root);
1774 if (search_path == NULL) {
1775 crm_warn(
"Will never find <NULL>");
1779 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
1780 if (strcmp((
const char *)a_child->name, search_path) == 0) {
1787 crm_warn(
"Could not find %s in %s.", search_path, name);
1788 }
else if (root != NULL) {
1789 crm_trace(
"Could not find %s in %s.", search_path, name);
1791 crm_trace(
"Could not find %s in <NULL>.", search_path);
1801 find_entity_by_attr_or_just_name(xmlNode *parent,
const char *node_name,
1802 const char *attr_n,
const char *attr_v)
1807 CRM_CHECK(attr_n == NULL || attr_v != NULL,
return NULL);
1809 for (child = __xml_first_child(parent); child != NULL; child = __xml_next(child)) {
1811 if (node_name == NULL || !strcmp((
const char *) child->name, node_name)) {
1821 attr_n ? attr_n :
"",
1823 attr_n ? attr_v :
"",
1824 crm_element_name(parent));
1832 return find_entity_by_attr_or_just_name(parent, node_name,
1840 crm_warn(
"No node to copy properties from");
1842 }
else if (target == NULL) {
1843 crm_err(
"No node to copy properties into");
1846 xmlAttrPtr pIter = NULL;
1848 for (pIter = pcmk__first_xml_attr(src); pIter != NULL; pIter = pIter->next) {
1849 const char *p_name = (
const char *)pIter->name;
1850 const char *p_value = pcmk__xml_attr_value(pIter);
1863 xmlNode *child = NULL;
1864 xmlAttrPtr pIter = NULL;
1866 for (pIter = pcmk__first_xml_attr(target); pIter != NULL; pIter = pIter->next) {
1867 const char *p_name = (
const char *)pIter->name;
1868 const char *p_value = pcmk__xml_attr_value(pIter);
1872 for (child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
1885 const char *old_value = NULL;
1887 if (value == NULL || name == NULL) {
1893 if (old_value == NULL) {
1895 goto set_unexpanded;
1897 }
else if (strstr(value, name) != value) {
1898 goto set_unexpanded;
1901 name_len = strlen(name);
1902 value_len = strlen(value);
1903 if (value_len < (name_len + 2)
1904 || value[name_len] !=
'+' || (value[name_len + 1] !=
'+' && value[name_len + 1] !=
'=')) {
1905 goto set_unexpanded;
1911 if (old_value != value) {
1915 if (value[name_len + 1] !=
'+') {
1916 const char *offset_s = value + (name_len + 2);
1920 int_value += offset;
1930 if (old_value == value) {
1947 doc = xmlNewDoc((
const xmlChar *)
"1.0");
1948 xmlDocSetRootElement(doc, node);
1949 xmlSetTreeDoc(node, doc);
1957 xmlNode *child = NULL;
1960 CRM_CHECK(src_node != NULL,
return NULL);
1962 child = xmlDocCopyNode(src_node, doc, 1);
1963 xmlAddChild(parent, child);
1964 crm_node_created(child);
1980 xmlNode *node = NULL;
1982 if (name == NULL || name[0] == 0) {
1983 CRM_CHECK(name != NULL && name[0] == 0,
return NULL);
1987 if (parent == NULL) {
1988 doc = xmlNewDoc((
const xmlChar *)
"1.0");
1989 node = xmlNewDocRawNode(doc, NULL, (
const xmlChar *)name, NULL);
1990 xmlDocSetRootElement(doc, node);
1994 node = xmlNewDocRawNode(doc, NULL, (
const xmlChar *)name, NULL);
1995 xmlAddChild(parent, node);
1997 crm_node_created(node);
2003 int offset,
size_t buffer_size)
2005 const char *
id =
ID(xml);
2007 if(offset == 0 && prefix == NULL && xml->parent) {
2013 offset += snprintf(buffer + offset, buffer_size - offset,
2014 "/%s[@id='%s']", (
const char *) xml->name,
id);
2015 }
else if(xml->name) {
2016 offset += snprintf(buffer + offset, buffer_size - offset,
2017 "/%s", (
const char *) xml->name);
2030 return strdup(buffer);
2048 free_xml_with_position(xmlNode * child,
int position)
2050 if (child != NULL) {
2051 xmlNode *top = NULL;
2052 xmlDoc *doc = child->doc;
2056 top = xmlDocGetRootElement(doc);
2059 if (doc != NULL && top == child) {
2078 sizeof(buffer)) > 0) {
2081 crm_trace(
"Deleting %s %p from %p", buffer, child, doc);
2083 deleted_obj->path = strdup(buffer);
2085 deleted_obj->position = -1;
2087 if (child->type == XML_COMMENT_NODE) {
2088 if (position >= 0) {
2089 deleted_obj->position = position;
2092 deleted_obj->position = __xml_offset(child);
2110 free_xml_with_position(child, -1);
2116 xmlDoc *doc = xmlNewDoc((
const xmlChar *)
"1.0");
2117 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
2119 xmlDocSetRootElement(doc, copy);
2120 xmlSetTreeDoc(copy, doc);
2125 crm_xml_err(
void *ctx,
const char *fmt, ...)
2126 G_GNUC_PRINTF(2, 3);
2129 crm_xml_err(
void *ctx,
const char *fmt, ...)
2132 static struct qb_log_callsite *xml_error_cs = NULL;
2134 if (xml_error_cs == NULL) {
2135 xml_error_cs = qb_log_callsite_get(
2140 if (xml_error_cs && xml_error_cs->targets) {
2142 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__,
"xml library error",
2144 "XML Error: ", fmt, ap);
2154 xmlNode *xml = NULL;
2155 xmlDocPtr output = NULL;
2156 xmlParserCtxtPtr ctxt = NULL;
2157 xmlErrorPtr last_error = NULL;
2159 if (input == NULL) {
2160 crm_err(
"Can't parse NULL input");
2165 ctxt = xmlNewParserCtxt();
2168 xmlCtxtResetLastError(ctxt);
2169 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2170 output = xmlCtxtReadDoc(ctxt, (
const xmlChar *) input, NULL, NULL,
2173 xml = xmlDocGetRootElement(output);
2175 last_error = xmlCtxtGetLastError(ctxt);
2176 if (last_error && last_error->code != XML_ERR_OK) {
2182 crm_warn(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
2183 last_error->domain, last_error->level, last_error->code, last_error->message);
2185 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
2188 }
else if (last_error->code != XML_ERR_DOCUMENT_END) {
2189 crm_err(
"Couldn't%s parse %d chars: %s", xml ?
" fully" :
"", (
int)strlen(input),
2196 int len = strlen(input);
2200 crm_warn(
"Parse error[+%.3d]: %.80s", lpc, input+lpc);
2208 xmlFreeParserCtxt(ctxt);
2215 size_t data_length = 0;
2216 size_t read_chars = 0;
2218 char *xml_buffer = NULL;
2219 xmlNode *xml_obj = NULL;
2229 xml_buffer = realloc_safe(xml_buffer, next);
2230 read_chars = fread(xml_buffer + data_length, 1,
XML_BUFFER_SIZE, stdin);
2231 data_length += read_chars;
2232 }
while (read_chars > 0);
2234 if (data_length == 0) {
2235 crm_warn(
"No XML supplied on stdin");
2240 xml_buffer[data_length] =
'\0';
2250 decompress_file(
const char *filename)
2252 char *buffer = NULL;
2256 size_t length = 0, read_len = 0;
2258 BZFILE *bz_file = NULL;
2259 FILE *input = fopen(filename,
"r");
2261 if (input == NULL) {
2262 crm_perror(LOG_ERR,
"Could not open %s for reading", filename);
2266 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
2269 BZ2_bzReadClose(&rc, bz_file);
2274 while (rc == BZ_OK) {
2276 read_len = BZ2_bzRead(&rc, bz_file, buffer + length,
XML_BUFFER_SIZE);
2278 crm_trace(
"Read %ld bytes from file: %d", (
long)read_len, rc);
2280 if (rc == BZ_OK || rc == BZ_STREAM_END) {
2285 buffer[length] =
'\0';
2287 if (rc != BZ_STREAM_END) {
2288 crm_err(
"Couldn't read compressed xml from file");
2293 BZ2_bzReadClose(&rc, bz_file);
2297 crm_err(
"Cannot read compressed files:" " bzlib was not available at compile time");
2305 xmlNode *iter = xml->children;
2308 xmlNode *next = iter->next;
2310 switch (iter->type) {
2316 case XML_ELEMENT_NODE:
2333 xmlNode *xml = NULL;
2334 xmlDocPtr output = NULL;
2335 gboolean uncompressed = TRUE;
2336 xmlParserCtxtPtr ctxt = NULL;
2337 xmlErrorPtr last_error = NULL;
2340 ctxt = xmlNewParserCtxt();
2343 xmlCtxtResetLastError(ctxt);
2344 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2350 if (filename == NULL) {
2352 output = xmlCtxtReadFd(ctxt, STDIN_FILENO,
"unknown.xml", NULL,
2355 }
else if (uncompressed) {
2359 char *input = decompress_file(filename);
2361 output = xmlCtxtReadDoc(ctxt, (
const xmlChar *) input, NULL, NULL,
2366 if (output && (xml = xmlDocGetRootElement(output))) {
2370 last_error = xmlCtxtGetLastError(ctxt);
2371 if (last_error && last_error->code != XML_ERR_OK) {
2377 crm_err(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
2378 last_error->domain, last_error->level, last_error->code, last_error->message);
2380 if (last_error && last_error->code != XML_ERR_OK) {
2381 crm_err(
"Couldn't%s parse %s", xml ?
" fully" :
"", filename);
2388 xmlFreeParserCtxt(ctxt);
2403 time_t now = time(NULL);
2404 char *now_str = ctime(&now);
2420 for (c =
id; *c; ++c) {
2445 va_start(ap, format);
2446 len = vasprintf(&
id, format, ap);
2456 write_xml_stream(xmlNode * xml_node,
const char *filename, FILE * stream, gboolean compress)
2459 char *buffer = NULL;
2460 unsigned int out = 0;
2464 crm_trace(
"Writing XML out to %s", filename);
2465 if (xml_node == NULL) {
2466 crm_err(
"Cannot write NULL to %s", filename);
2481 unsigned int in = 0;
2482 BZFILE *bz_file = NULL;
2484 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
2486 crm_err(
"bzWriteOpen failed: %d", rc);
2488 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
2490 crm_err(
"bzWrite() failed: %d", rc);
2495 BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
2497 crm_err(
"bzWriteClose() failed: %d", rc);
2500 crm_trace(
"%s: In: %d, out: %d", filename, in, out);
2504 crm_err(
"Cannot write compressed files:" " bzlib was not available at compile time");
2509 res = fprintf(stream,
"%s", buffer);
2511 crm_perror(LOG_ERR,
"Cannot write output to %s", filename);
2518 if (fflush(stream) != 0) {
2519 crm_perror(LOG_ERR,
"fflush for %s failed", filename);
2524 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
2525 crm_perror(LOG_ERR,
"fsync for %s failed", filename);
2531 crm_trace(
"Saved %d bytes to the Cib as XML", res);
2538 write_xml_fd(xmlNode * xml_node,
const char *filename,
int fd, gboolean compress)
2540 FILE *stream = NULL;
2543 stream = fdopen(fd,
"w");
2544 return write_xml_stream(xml_node, filename, stream, compress);
2550 FILE *stream = NULL;
2552 stream = fopen(filename,
"w");
2554 return write_xml_stream(xml_node, filename, stream, compress);
2562 return __xml_first_child(tmp);
2575 crm_xml_escape_shuffle(
char *text,
int start,
int *length,
const char *replace)
2578 int offset = strlen(replace) - 1;
2581 text = realloc_safe(text, *length);
2583 for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
2584 text[lpc] = text[lpc - offset];
2587 memcpy(text + start, replace, offset + 1);
2596 int length = 1 + strlen(text);
2597 char *copy = strdup(text);
2614 for (index = 0; index < length; index++) {
2615 switch (copy[index]) {
2619 copy = crm_xml_escape_shuffle(copy, index, &length,
"<");
2623 copy = crm_xml_escape_shuffle(copy, index, &length,
">");
2627 copy = crm_xml_escape_shuffle(copy, index, &length,
""");
2631 copy = crm_xml_escape_shuffle(copy, index, &length,
"'");
2635 copy = crm_xml_escape_shuffle(copy, index, &length,
"&");
2640 copy = crm_xml_escape_shuffle(copy, index, &length,
" ");
2645 copy = crm_xml_escape_shuffle(copy, index, &length,
"\\n");
2649 copy = crm_xml_escape_shuffle(copy, index, &length,
"\\r");
2659 if(copy[index] <
' ' || copy[index] >
'~') {
2663 copy = crm_xml_escape_shuffle(copy, index, &length, replace);
2677 dump_xml_attr(xmlAttrPtr attr,
int options,
char **buffer,
int *offset,
int *max)
2679 char *p_value = NULL;
2680 const char *p_name = NULL;
2684 if (attr == NULL || attr->children == NULL) {
2693 p_name = (
const char *)attr->name;
2695 buffer_print(*buffer, *max, *offset,
" %s=\"%s\"", p_name, p_value);
2700 __xml_log_element(
int log_level,
const char *file,
const char *
function,
int line,
2701 const char *prefix, xmlNode *
data,
int depth,
int options)
2705 const char *name = NULL;
2706 const char *hidden = NULL;
2708 xmlNode *child = NULL;
2709 xmlAttrPtr pIter = NULL;
2715 name = crm_element_name(
data);
2718 char *buffer = NULL;
2720 insert_prefix(options, &buffer, &offset, &max, depth);
2722 if (
data->type == XML_COMMENT_NODE) {
2729 for (pIter = pcmk__first_xml_attr(
data); pIter != NULL; pIter = pIter->next) {
2731 const char *p_name = (
const char *)pIter->name;
2732 const char *p_value = pcmk__xml_attr_value(pIter);
2733 char *p_copy = NULL;
2742 }
else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
2743 p_copy = strdup(
"*****");
2749 buffer_print(buffer, max, offset,
" %s=\"%s\"", p_name, p_copy);
2764 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
2768 if(
data->type == XML_COMMENT_NODE) {
2778 for (child = __xml_first_child(
data); child != NULL; child = __xml_next(child)) {
2784 char *buffer = NULL;
2786 insert_prefix(options, &buffer, &offset, &max, depth);
2789 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
2795 __xml_log_change_element(
int log_level,
const char *file,
const char *
function,
int line,
2796 const char *prefix, xmlNode *
data,
int depth,
int options)
2799 char *prefix_m = NULL;
2800 xmlNode *child = NULL;
2801 xmlAttrPtr pIter = NULL;
2809 prefix_m = strdup(prefix);
2814 __xml_log_element(log_level, file,
function, line,
2818 char *spaces = calloc(80, 1);
2819 int s_count = 0, s_max = 80;
2820 char *prefix_del = NULL;
2821 char *prefix_moved = NULL;
2822 const char *
flags = prefix;
2824 insert_prefix(options, &spaces, &s_count, &s_max, depth);
2825 prefix_del = strdup(prefix);
2826 prefix_del[0] =
'-';
2827 prefix_del[1] =
'-';
2828 prefix_moved = strdup(prefix);
2829 prefix_moved[1] =
'~';
2832 flags = prefix_moved;
2837 __xml_log_element(log_level, file,
function, line,
2840 for (pIter = pcmk__first_xml_attr(
data); pIter != NULL; pIter = pIter->next) {
2841 const char *aname = (
const char*)pIter->name;
2843 p = pIter->_private;
2848 "%s %s @%s=%s", flags, spaces, aname, value);
2860 flags = prefix_moved;
2866 "%s %s @%s=%s", flags, spaces, aname, value);
2873 for (child = __xml_first_child(
data); child != NULL; child = __xml_next(child)) {
2874 __xml_log_change_element(log_level, file,
function, line, prefix, child, depth + 1, options);
2877 __xml_log_element(log_level, file,
function, line,
2881 for (child = __xml_first_child(
data); child != NULL; child = __xml_next(child)) {
2882 __xml_log_change_element(log_level, file,
function, line, prefix, child, depth + 1, options);
2892 const char *prefix, xmlNode *
data,
int depth,
int options)
2894 xmlNode *a_child = NULL;
2896 char *prefix_m = NULL;
2898 if (prefix == NULL) {
2905 "No data to dump as XML");
2910 __xml_log_change_element(log_level, file,
function, line, prefix, data, depth, options);
2918 prefix_m = strdup(prefix);
2925 prefix_m = strdup(prefix);
2934 for (a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
2935 log_data_element(log_level, file,
function, line, prefix, a_child, depth + 1, options);
2938 __xml_log_element(log_level, file,
function, line, prefix, data, depth,
2945 dump_filtered_xml(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max)
2948 xmlAttrPtr xIter = NULL;
2949 static int filter_len =
DIMOF(filter);
2951 for (lpc = 0; options && lpc < filter_len; lpc++) {
2952 filter[lpc].found = FALSE;
2955 for (xIter = pcmk__first_xml_attr(
data); xIter != NULL; xIter = xIter->next) {
2957 const char *p_name = (
const char *)xIter->name;
2959 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
2960 if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].
string) == 0) {
2961 filter[lpc].found = TRUE;
2967 if (skip == FALSE) {
2968 dump_xml_attr(xIter, options, buffer, offset, max);
2974 dump_xml_element(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
2976 const char *name = NULL;
2987 if (*buffer == NULL) {
2992 name = crm_element_name(
data);
2995 insert_prefix(options, buffer, offset, max, depth);
2999 dump_filtered_xml(
data, options, buffer, offset, max);
3002 xmlAttrPtr xIter = NULL;
3004 for (xIter = pcmk__first_xml_attr(
data); xIter != NULL; xIter = xIter->next) {
3005 dump_xml_attr(xIter, options, buffer, offset, max);
3009 if (
data->children == NULL) {
3020 if (
data->children) {
3021 xmlNode *xChild = NULL;
3022 for(xChild =
data->children; xChild != NULL; xChild = xChild->next) {
3023 crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
3026 insert_prefix(options, buffer, offset, max, depth);
3029 if (options & xml_log_option_formatted) {
3036 dump_xml_text(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3047 if (*buffer == NULL) {
3052 insert_prefix(options, buffer, offset, max, depth);
3063 dump_xml_comment(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3074 if (*buffer == NULL) {
3079 insert_prefix(options, buffer, offset, max, depth);
3114 xmlBuffer *xml_buffer = NULL;
3122 xml_buffer = xmlBufferCreate();
3133 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_DOUBLEIT);
3137 *buffer = strdup((
char *)xml_buffer->content);
3141 if ((now + 1) < next) {
3143 crm_err(
"xmlNodeDump() -> %dbytes took %ds", *max, next - now);
3146 xmlBufferFree(xml_buffer);
3151 switch(data->type) {
3152 case XML_ELEMENT_NODE:
3154 dump_xml_element(data, options, buffer, offset, max, depth);
3159 dump_xml_text(data, options, buffer, offset, max, depth);
3162 case XML_COMMENT_NODE:
3163 dump_xml_comment(data, options, buffer, offset, max, depth);
3166 crm_warn(
"Unhandled type: %d", data->type);
3202 char *buffer = NULL;
3203 int offset = 0, max = 0;
3212 char *buffer = NULL;
3213 int offset = 0, max = 0;
3222 char *buffer = NULL;
3223 int offset = 0, max = 0;
3225 crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
3232 if (xml_root != NULL && xml_root->children != NULL) {
3242 crm_trace(
"Cannot remove %s from %s", name, obj->name);
3247 xmlAttr *attr = xmlHasProp(obj, (
const xmlChar *)name);
3255 xmlUnsetProp(obj, (
const xmlChar *)name);
3262 xmlNode *child = NULL;
3267 for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
3277 if (filename == NULL) {
3285 crm_info(
"Saving %s to %s", desc, filename);
3293 gboolean result = TRUE;
3294 int root_nodes_seen = 0;
3295 static struct qb_log_callsite *digest_cs = NULL;
3299 xmlNode *child_diff = NULL;
3301 xmlNode *removed =
find_xml_node(diff,
"diff-removed", FALSE);
3304 if (digest_cs == NULL) {
3306 qb_log_callsite_get(__func__, __FILE__,
"diff-digest",
LOG_TRACE, __LINE__,
3311 for (child_diff = __xml_first_child(removed); child_diff != NULL;
3312 child_diff = __xml_next(child_diff)) {
3313 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3314 if (root_nodes_seen == 0) {
3320 if (root_nodes_seen == 0) {
3323 }
else if (root_nodes_seen > 1) {
3324 crm_err(
"(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3328 root_nodes_seen = 0;
3331 xmlNode *child_diff = NULL;
3333 for (child_diff = __xml_first_child(added); child_diff != NULL;
3334 child_diff = __xml_next(child_diff)) {
3335 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3336 if (root_nodes_seen == 0) {
3343 if (root_nodes_seen > 1) {
3344 crm_err(
"(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3347 }
else if (result && digest) {
3348 char *new_digest = NULL;
3353 crm_info(
"Digest mis-match: expected %s, calculated %s", digest, new_digest);
3356 crm_trace(
"%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
3357 if (digest_cs && digest_cs->targets) {
3364 crm_trace(
"Digest matched: expected %s, calculated %s", digest, new_digest);
3368 }
else if (result) {
3376 __xml_diff_object(xmlNode * old, xmlNode *
new,
bool check_top)
3378 xmlNode *cIter = NULL;
3379 xmlAttr *pIter = NULL;
3384 crm_node_created(
new);
3398 for (pIter = pcmk__first_xml_attr(
new); pIter != NULL; pIter = pIter->next) {
3405 for (pIter = pcmk__first_xml_attr(old); pIter != NULL; ) {
3406 xmlAttr *prop = pIter;
3408 const char *name = (
const char *)pIter->name;
3410 xmlAttr *exists = xmlHasProp(
new, pIter->name);
3412 pIter = pIter->next;
3413 if(exists == NULL) {
3414 p =
new->doc->_private;
3418 exists = xmlSetProp(
new, (
const xmlChar *)name, (
const xmlChar *)old_value);
3421 p = exists->_private;
3424 crm_trace(
"Lost %s@%s=%s", old->name, name, old_value);
3428 int p_new = __xml_offset((xmlNode*)exists);
3429 int p_old = __xml_offset((xmlNode*)prop);
3432 p = exists->_private;
3435 if(strcmp(value, old_value) != 0) {
3441 crm_trace(
"Modified %s@%s %s->%s", old->name, name, old_value, vcopy);
3442 xmlSetProp(
new, prop->name, (
const xmlChar *)old_value);
3446 }
else if ((p_old != p_new)
3448 crm_info(
"Moved %s@%s (%d -> %d)", old->name, name, p_old, p_new);
3449 __xml_node_dirty(
new);
3457 p = exists->_private;
3464 for (pIter = pcmk__first_xml_attr(
new); pIter != NULL; ) {
3465 xmlAttr *prop = pIter;
3468 pIter = pIter->next;
3470 char *name = strdup((
const char *)prop->name);
3473 crm_trace(
"Created %s@%s=%s", new->name, name, value);
3478 xmlUnsetProp(
new, prop->name);
3486 for (cIter = __xml_first_child(old); cIter != NULL; ) {
3487 xmlNode *old_child = cIter;
3488 xmlNode *new_child = find_element(
new, cIter, TRUE);
3490 cIter = __xml_next(cIter);
3492 __xml_diff_object(old_child, new_child, TRUE);
3497 xmlNode *top = xmlDocGetRootElement(candidate->doc);
3499 __xml_node_clean(candidate);
3502 free_xml_with_position(candidate, __xml_offset(old_child));
3504 if (find_element(
new, old_child, TRUE) == NULL) {
3512 for (cIter = __xml_first_child(
new); cIter != NULL; ) {
3513 xmlNode *new_child = cIter;
3514 xmlNode *old_child = find_element(old, cIter, TRUE);
3516 cIter = __xml_next(cIter);
3517 if(old_child == NULL) {
3520 __xml_diff_object(old_child, new_child, TRUE);
3524 int p_new = __xml_offset(new_child);
3525 int p_old = __xml_offset(old_child);
3527 if(p_old != p_new) {
3530 crm_info(
"%s.%s moved from %d to %d",
3531 new_child->name,
ID(new_child), p_old, p_new);
3532 __xml_node_dirty(
new);
3536 p = old_child->_private;
3538 p = new_child->_private;
3563 __xml_diff_object(old,
new, FALSE);
3569 xmlNode *tmp1 = NULL;
3586 if (added->children == NULL && removed->children == NULL) {
3597 xmlNode *cIter = NULL;
3598 xmlAttrPtr pIter = NULL;
3599 gboolean can_prune = TRUE;
3600 const char *name = crm_element_name(xml_node);
3609 for (pIter = pcmk__first_xml_attr(xml_node); pIter != NULL; pIter = pIter->next) {
3610 const char *p_name = (
const char *)pIter->name;
3618 cIter = __xml_first_child(xml_node);
3620 xmlNode *child = cIter;
3622 cIter = __xml_next(cIter);
3634 xmlNode * xml_node, xmlNode * parent)
3637 xmlNode *child = NULL;
3638 xmlAttrPtr pIter = NULL;
3639 xmlNode *new_parent = parent;
3640 const char *name = crm_element_name(xml_node);
3642 CRM_CHECK(xml_node != NULL && name != NULL,
return);
3645 for (pIter = pcmk__first_xml_attr(xml_node); pIter != NULL;
3646 pIter = pIter->next) {
3648 const char *p_name = (
const char *)pIter->name;
3649 const char *p_value = pcmk__xml_attr_value(pIter);
3651 lower_bound = context;
3655 if (lower_bound >= 0 || upper_bound >= 0) {
3661 if (upper_bound >= 0) {
3670 for (child = __xml_first_child(us); child != NULL; child = __xml_next(child)) {
3682 if (xml_node->properties) {
3685 }
else if (depth < context) {
3686 xmlNode *child = NULL;
3688 for (child = __xml_first_child(xml_node); child != NULL; child = __xml_next(child)) {
3698 find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact)
3700 xmlNode *a_child = NULL;
3701 int search_offset = __xml_offset(search_comment);
3703 CRM_CHECK(search_comment->type == XML_COMMENT_NODE,
return NULL);
3705 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
3707 int offset = __xml_offset(a_child);
3710 if (offset < search_offset) {
3713 }
else if (offset > search_offset) {
3722 if (a_child->type == XML_COMMENT_NODE
3723 &&
safe_str_eq((
const char *)a_child->content, (
const char *)search_comment->content)) {
3735 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
3739 CRM_CHECK(left->type == XML_COMMENT_NODE,
return NULL);
3742 ||
safe_str_neq((
const char *)left->content, (
const char *)right->content)) {
3743 xmlNode *deleted = NULL;
3756 gboolean full, gboolean * changed,
const char *marker)
3758 gboolean dummy = FALSE;
3759 gboolean skip = FALSE;
3760 xmlNode *diff = NULL;
3761 xmlNode *right_child = NULL;
3762 xmlNode *left_child = NULL;
3763 xmlAttrPtr xIter = NULL;
3765 const char *
id = NULL;
3766 const char *name = NULL;
3767 const char *value = NULL;
3768 const char *right_val = NULL;
3771 static int filter_len =
DIMOF(filter);
3773 if (changed == NULL) {
3781 if (left->type == XML_COMMENT_NODE) {
3782 return subtract_xml_comment(parent, left, right, changed);
3786 if (right == NULL) {
3787 xmlNode *deleted = NULL;
3789 crm_trace(
"Processing <%s id=%s> (complete copy)", crm_element_name(left),
id);
3797 name = crm_element_name(left);
3803 if (value != NULL && strcmp(value,
"removed:top") == 0) {
3804 crm_trace(
"We are the root of the deletion: %s.id=%s", name,
id);
3813 for (lpc = 0; lpc < filter_len; lpc++) {
3814 filter[lpc].found = FALSE;
3818 for (left_child = __xml_first_child(left); left_child != NULL;
3819 left_child = __xml_next(left_child)) {
3820 gboolean child_changed = FALSE;
3822 right_child = find_element(right, left_child, FALSE);
3824 if (child_changed) {
3829 if (*changed == FALSE) {
3833 xmlAttrPtr pIter = NULL;
3835 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3836 const char *p_name = (
const char *)pIter->name;
3837 const char *p_value = pcmk__xml_attr_value(pIter);
3839 xmlSetProp(diff, (
const xmlChar *)p_name, (
const xmlChar *)p_value);
3846 xmlSetProp(diff, (
const xmlChar *)
XML_ATTR_ID, (
const xmlChar *)
id);
3850 for (xIter = pcmk__first_xml_attr(left); xIter != NULL; xIter = xIter->next) {
3851 const char *prop_name = (
const char *)xIter->name;
3852 xmlAttrPtr right_attr = NULL;
3860 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
3861 if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].
string) == 0) {
3862 filter[lpc].found = TRUE;
3872 right_attr = xmlHasProp(right, (
const xmlChar *)prop_name);
3874 p = right_attr->_private;
3882 xmlAttrPtr pIter = NULL;
3884 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3885 const char *p_name = (
const char *)pIter->name;
3886 const char *p_value = pcmk__xml_attr_value(pIter);
3888 xmlSetProp(diff, (
const xmlChar *)p_name, (
const xmlChar *)p_value);
3895 xmlSetProp(diff, (
const xmlChar *)prop_name, (
const xmlChar *)value);
3903 if (strcmp(left_value, right_val) == 0) {
3909 xmlAttrPtr pIter = NULL;
3911 crm_trace(
"Changes detected to %s in <%s id=%s>", prop_name,
3912 crm_element_name(left),
id);
3913 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3914 const char *p_name = (
const char *)pIter->name;
3915 const char *p_value = pcmk__xml_attr_value(pIter);
3917 xmlSetProp(diff, (
const xmlChar *)p_name, (
const xmlChar *)p_value);
3922 crm_trace(
"Changes detected to %s (%s -> %s) in <%s id=%s>",
3923 prop_name, left_value, right_val, crm_element_name(left),
id);
3930 if (*changed == FALSE) {
3934 }
else if (full == FALSE &&
id) {
3942 add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update)
3945 CRM_CHECK(update->type == XML_COMMENT_NODE,
return 0);
3947 if (target == NULL) {
3948 target = find_xml_comment(parent, update, FALSE);
3951 if (target == NULL) {
3955 }
else if (
safe_str_neq((
const char *)target->content, (
const char *)update->content)) {
3956 xmlFree(target->content);
3957 target->content = xmlStrdup(update->content);
3964 add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff)
3966 xmlNode *a_child = NULL;
3967 const char *object_name = NULL,
3968 *object_href = NULL,
3969 *object_href_val = NULL;
3978 if (update->type == XML_COMMENT_NODE) {
3979 return add_xml_comment(parent, target, update);
3982 object_name = crm_element_name(update);
3983 object_href_val =
ID(update);
3984 if (object_href_val != NULL) {
3991 CRM_CHECK(object_name != NULL,
return 0);
3992 CRM_CHECK(target != NULL || parent != NULL,
return 0);
3994 if (target == NULL) {
3995 target = find_entity_by_attr_or_just_name(parent, object_name,
3996 object_href, object_href_val);
3999 if (target == NULL) {
4002 #if XML_PARSER_DEBUG 4004 object_href ?
" " :
"",
4005 object_href ? object_href :
"",
4006 object_href ?
"=" :
"",
4007 object_href ? object_href_val :
"");
4011 object_href ?
" " :
"",
4012 object_href ? object_href :
"",
4013 object_href ?
"=" :
"",
4014 object_href ? object_href_val :
"");
4020 if (as_diff == FALSE) {
4026 xmlAttrPtr pIter = NULL;
4028 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
4029 const char *p_name = (
const char *)pIter->name;
4030 const char *p_value = pcmk__xml_attr_value(pIter);
4033 xmlUnsetProp(target, (
const xmlChar *)p_name);
4034 xmlSetProp(target, (
const xmlChar *)p_name, (
const xmlChar *)p_value);
4038 for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
4039 #if XML_PARSER_DEBUG 4041 object_href ?
" " :
"",
4042 object_href ? object_href :
"",
4043 object_href ?
"=" :
"",
4044 object_href ? object_href_val :
"");
4049 #if XML_PARSER_DEBUG 4051 object_href ?
" " :
"",
4052 object_href ? object_href :
"",
4053 object_href ?
"=" :
"",
4054 object_href ? object_href_val :
"");
4062 gboolean can_update = TRUE;
4063 xmlNode *child_of_child = NULL;
4066 CRM_CHECK(to_update != NULL,
return FALSE);
4068 if (
safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
4074 }
else if (can_update) {
4075 #if XML_PARSER_DEBUG 4081 for (child_of_child = __xml_first_child(child); child_of_child != NULL;
4082 child_of_child = __xml_next(child_of_child)) {
4095 const char *tag,
const char *field,
const char *value, gboolean search_matches)
4097 int match_found = 0;
4100 CRM_CHECK(children != NULL,
return FALSE);
4102 if (tag != NULL &&
safe_str_neq(tag, crm_element_name(root))) {
4107 if (*children == NULL) {
4114 if (search_matches || match_found == 0) {
4115 xmlNode *child = NULL;
4117 for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
4118 match_found +=
find_xml_children(children, child, tag, field, value, search_matches);
4128 gboolean can_delete = FALSE;
4129 xmlNode *child_of_child = NULL;
4131 const char *up_id = NULL;
4132 const char *child_id = NULL;
4133 const char *right_val = NULL;
4136 CRM_CHECK(update != NULL,
return FALSE);
4139 child_id =
ID(child);
4141 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
4144 if (
safe_str_neq(crm_element_name(update), crm_element_name(child))) {
4147 if (can_delete && delete_only) {
4148 xmlAttrPtr pIter = NULL;
4150 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
4151 const char *p_name = (
const char *)pIter->name;
4152 const char *p_value = pcmk__xml_attr_value(pIter);
4161 if (can_delete && parent != NULL) {
4163 if (delete_only || update == NULL) {
4168 xmlDoc *doc = tmp->doc;
4169 xmlNode *old = NULL;
4172 old = xmlReplaceNode(child, tmp);
4180 xmlDocSetRootElement(doc, old);
4186 }
else if (can_delete) {
4191 child_of_child = __xml_first_child(child);
4192 while (child_of_child) {
4193 xmlNode *next = __xml_next(child_of_child);
4199 child_of_child = NULL;
4201 child_of_child = next;
4211 xmlNode *child = NULL;
4212 GSList *nvpairs = NULL;
4213 xmlNode *result = NULL;
4214 const char *name = NULL;
4218 name = crm_element_name(input);
4227 for (child = __xml_first_child(input); child != NULL;
4228 child = __xml_next(child)) {
4243 xmlNode *match = NULL;
4245 for (match = __xml_first_child_element(parent); match != NULL;
4246 match = __xml_next_element(match)) {
4252 if (name == NULL || strcmp((
const char *)match->name, name) == 0) {
4269 xmlNode *match = __xml_next_element(sibling);
4270 const char *name = crm_element_name(sibling);
4272 while (match != NULL) {
4273 if (!strcmp(crm_element_name(match), name)) {
4276 match = __xml_next_element(match);
4284 static bool init = TRUE;
4293 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
4296 xmlDeregisterNodeDefault(pcmkDeregisterNode);
4297 xmlRegisterNodeDefault(pcmkRegisterNode);
4306 crm_info(
"Cleaning up memory from libxml2");
4311 #define XPATH_MAX 512 4316 const char *tag = NULL;
4317 const char *ref = NULL;
4318 xmlNode *result = input;
4319 char *xpath_string = NULL;
4321 if (result == NULL) {
4324 }
else if (top == NULL) {
4328 tag = crm_element_name(result);
4336 offset += snprintf(xpath_string + offset,
XPATH_MAX - offset,
"//%s[@id='%s']", tag, ref);
4340 if (result == NULL) {
4341 char *nodePath = (
char *)xmlGetNodePath(top);
4343 crm_err(
"No match for %s found in %s: Invalid configuration", xpath_string,
#define CRM_CHECK(expr, failure_action)
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
gboolean daemon_option_enabled(const char *daemon, const char *option)
#define XML_ATTR_UPDATE_ORIG
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
xmlNode * crm_next_same_xml(xmlNode *sibling)
Get next instance of same XML tag.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
void crm_schema_init(void)
#define crm_notice(fmt, args...)
#define XML_ATTR_UPDATE_CLIENT
#define PCMK__XML_PARSE_OPTS
xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
gboolean safe_str_neq(const char *a, const char *b)
char * crm_generate_uuid(void)
int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update, gboolean as_diff)
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, int options)
#define XML_ATTR_NUMUPDATES
void pcmk__free_acls(GList *acls)
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
#define pcmk_err_old_data
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
#define XML_ATTR_UPDATE_USER
int char2score(const char *score)
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
void fix_plus_plus_recursive(xmlNode *target)
#define buffer_print(buffer, max, offset, fmt, args...)
void crm_schema_cleanup(void)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
void pcmk_free_xml_subtree(xmlNode *xml)
#define XML_NVPAIR_ATTR_NAME
void purge_diff_markers(xmlNode *a_node)
int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
xmlNode * stdin2xml(void)
int get_attr_name(const char *input, size_t offset, size_t max)
#define CRM_LOG_ASSERT(expr)
struct xml_change_obj_s xml_change_obj_t
#define do_crm_log_alias(level, file, function, line, fmt, args...)
Log a message as if it came from a different code location.
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
#define clear_bit(word, bit)
unsigned int crm_trace_nonlog
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node's attributes.
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
xmlNode * first_named_child(xmlNode *parent, const char *name)
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
char * xml_get_path(xmlNode *xml)
#define CRM_XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
#define XML_ATTR_GENERATION
xmlNode * filename2xml(const char *filename)
void pcmk__post_process_acl(xmlNode *xml, bool check_top)
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
#define pcmk_err_diff_failed
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
#define pcmk_err_diff_resync
#define crm_warn(fmt, args...)
#define set_bit(word, bit)
xmlNode * copy_xml(xmlNode *src)
void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent)
#define crm_debug(fmt, args...)
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
void xml_log_patchset(uint8_t log_level, const char *function, xmlNode *patchset)
void free_xml(xmlNode *child)
#define crm_trace(fmt, args...)
#define crm_log_xml_explicit(xml, text)
#define XML_PRIVATE_MAGIC
#define crm_log_xml_debug(xml, text)
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Wrappers for and extensions to libxml2.
void crm_xml_dump(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
#define crm_log_xml_warn(xml, text)
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
#define XML_DIFF_POSITION
#define XML_TAG_RESOURCE_REF
void xml_acl_disable(xmlNode *xml)
void crm_xml_cleanup(void)
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
xmlDoc * getDocPtr(xmlNode *node)
char * dump_xml_formatted(xmlNode *an_xml_node)
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
void xml_calculate_changes(xmlNode *old, xmlNode *new)
const char * crm_xml_add_last_written(xmlNode *xml_node)
xmlNode * string2xml(const char *input)
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
gboolean crm_ends_with_ext(const char *s, const char *match)
void xml_log_changes(uint8_t log_level, const char *function, xmlNode *xml)
void crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
xmlNode * get_message_xml(xmlNode *msg, const char *field)
char * dump_xml_unformatted(xmlNode *an_xml_node)
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
void copy_in_properties(xmlNode *target, xmlNode *src)
#define crm_log_xml_err(xml, text)
void pcmk__set_xml_flag(xmlNode *xml, enum xml_private_flags flag)
#define crm_perror(level, fmt, args...)
Log a system error message.
void strip_text_nodes(xmlNode *xml)
gboolean xml_has_children(const xmlNode *xml_root)
#define crm_err(fmt, args...)
#define XML_CIB_ATTR_WRITTEN
#define XML_ACL_TAG_ROLE_REFv1
int get_attr_value(const char *input, size_t offset, size_t max)
void pcmk__apply_acl(xmlNode *xml)
xmlNode * find_xml_node(xmlNode *root, const char *search_path, gboolean must_find)
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
void xml_accept_changes(xmlNode *xml)
int compare_version(const char *version1, const char *version2)
#define crm_log_xml_info(xml, text)
#define XML_ATTR_GENERATION_ADMIN
#define XML_NVPAIR_ATTR_VALUE
#define XML_ATTR_CRM_VERSION
void crm_destroy_xml(gpointer data)
xmlNode destructor which can be used in glib collections
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
void pcmk__mark_xml_attr_dirty(xmlAttr *a)
char * crm_xml_escape(const char *text)
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
int get_tag_name(const char *input, size_t offset, size_t max)
xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, gboolean *changed, const char *marker)
#define XML_CIB_TAG_OBJ_REF
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
#define crm_log_xml_trace(xml, text)
bool xml_tracking_changes(xmlNode *xml)
#define XML_ACL_TAG_ROLE_REF
gboolean apply_xml_diff(xmlNode *old, xmlNode *diff, xmlNode **new)
#define XML_CIB_TAG_CONFIGURATION
#define safe_str_eq(a, b)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
int in_upper_context(int depth, int context, xmlNode *xml_node)
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
void xml_remove_prop(xmlNode *obj, const char *name)
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
#define crm_info(fmt, args...)
gboolean can_prune_leaf(xmlNode *xml_node)
bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
bool xml_document_dirty(xmlNode *xml)
struct xml_deleted_obj_s xml_deleted_obj_t