13 #include <sys/types.h>
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
33 #define XML_BUFFER_SIZE 4096
34 #define XML_PARSER_DEBUG 0
41 typedef struct xml_deleted_obj_s {
48 static filter_t filter[] = {
57 static xmlNode *subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right, gboolean * changed);
58 static xmlNode *find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact);
59 static int add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update);
61 #define CHUNK_SIZE 1024
66 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
70 }
else if (lazy && is_not_set(((
xml_private_t *)xml->doc->_private)->flags,
77 #define buffer_print(buffer, max, offset, fmt, args...) do { \
80 rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \
82 if(buffer && rc < 0) { \
83 crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \
84 (buffer)[(offset)] = 0; \
86 } else if(rc >= ((max) - (offset))) { \
88 (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \
89 tmp = realloc_safe((buffer), (max)); \
99 insert_prefix(
int options,
char **buffer,
int *offset,
int *max,
int depth)
102 size_t spaces = 2 * depth;
104 if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
106 (*buffer) = realloc_safe((*buffer), (*max));
108 memset((*buffer) + (*offset),
' ', spaces);
114 set_parent_flag(xmlNode *xml,
long flag)
117 for(; xml; xml = xml->parent) {
133 if(xml && xml->doc && xml->doc->_private){
143 __xml_node_dirty(xmlNode *xml)
150 __xml_node_clean(xmlNode *xml)
152 xmlNode *cIter = NULL;
159 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
160 __xml_node_clean(cIter);
165 crm_node_created(xmlNode *xml)
167 xmlNode *cIter = NULL;
173 __xml_node_dirty(xml);
176 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
177 crm_node_created(cIter);
185 xmlNode *parent = a->parent;
194 __xml_node_dirty(parent);
197 int get_tag_name(
const char *input,
size_t offset,
size_t max);
198 int get_attr_name(
const char *input,
size_t offset,
size_t max);
202 static int add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff);
204 #define XML_PRIVATE_MAGIC (long) 0x81726354
207 __xml_deleted_obj_free(
void *
data)
212 free(deleted_obj->path);
232 g_list_free_full(p->
deleted_objs, __xml_deleted_obj_free);
242 __xml_private_clean(p);
247 pcmkDeregisterNode(xmlNodePtr node)
256 if (node->type != XML_DOCUMENT_NODE || node->name == NULL
257 || node->name[0] !=
' ') {
258 __xml_private_free(node->_private);
263 pcmkRegisterNode(xmlNodePtr node)
268 case XML_ELEMENT_NODE:
269 case XML_DOCUMENT_NODE:
270 case XML_ATTRIBUTE_NODE:
271 case XML_COMMENT_NODE:
280 case XML_CDATA_SECTION_NODE:
284 crm_trace(
"Ignoring %p %d", node, node->type);
294 __xml_node_dirty(node);
302 crm_trace(
"Tracking changes%s to %p", enforce_acls?
" with ACLs":
"", xml);
305 if(acl_source == NULL) {
327 if(xml != NULL && xml->doc && xml->doc->_private) {
383 static int __xml_offset(xmlNode *xml)
386 xmlNode *cIter = NULL;
388 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
399 static int __xml_offset_no_deletions(xmlNode *xml)
402 xmlNode *cIter = NULL;
404 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
416 __xml_build_changes(xmlNode * xml, xmlNode *patchset)
418 xmlNode *cIter = NULL;
419 xmlAttr *pIter = NULL;
420 xmlNode *change = NULL;
428 sizeof(buffer)) > 0) {
429 int position = __xml_offset_no_deletions(xml);
442 for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
443 xmlNode *attr = NULL;
455 sizeof(buffer)) > 0) {
480 xmlNode *result = NULL;
485 for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
490 crm_xml_add(result, (
const char *)pIter->name, value);
495 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
496 __xml_build_changes(cIter, patchset);
504 crm_trace(
"%s.%s moved to position %d", xml->name,
ID(xml), __xml_offset(xml));
506 sizeof(buffer)) > 0) {
517 __xml_accept_changes(xmlNode * xml)
519 xmlNode *cIter = NULL;
520 xmlAttr *pIter = NULL;
524 pIter = pcmk__first_xml_attr(xml);
526 while (pIter != NULL) {
527 const xmlChar *name = pIter->name;
540 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
541 __xml_accept_changes(cIter);
546 is_config_change(xmlNode *xml)
553 p = config->_private;
559 if(xml->doc && xml->doc->_private) {
560 p = xml->doc->_private;
561 for(gIter = p->
deleted_objs; gIter; gIter = gIter->next) {
574 xml_repair_v1_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
578 xmlNode *diff_child = NULL;
580 const char *tag = NULL;
582 const char *vfields[] = {
588 if (local_diff == NULL) {
593 tag =
"diff-removed";
595 if (diff_child == NULL) {
605 for(lpc = 0; last && lpc <
DIMOF(vfields); lpc++){
609 if(changed || lpc == 2) {
616 if (diff_child == NULL) {
626 for(lpc = 0; next && lpc <
DIMOF(vfields); lpc++){
633 xmlAttrPtr xIter = NULL;
635 for (xIter = next->properties; xIter; xIter = xIter->next) {
636 const char *p_name = (
const char *)xIter->name;
647 xml_create_patchset_v1(xmlNode *source, xmlNode *target,
bool config,
bool suppress)
653 xml_repair_v1_diff(source, target, patchset, config);
660 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
668 xmlNode *patchset = NULL;
669 const char *vfields[] = {
681 doc = target->doc->_private;
689 for(lpc = 0; lpc <
DIMOF(vfields); lpc++){
699 for(lpc = 0; lpc <
DIMOF(vfields); lpc++){
708 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
714 if (deleted_obj->position >= 0) {
719 __xml_build_changes(target, patchset);
724 xml_create_patchset(
int format, xmlNode *source, xmlNode *target,
bool *config_changed,
bool manage_version)
728 xmlNode *patch = NULL;
737 config = is_config_change(target);
739 *config_changed = config;
742 if(manage_version && config) {
749 }
else if(manage_version) {
762 crm_trace(
"Using patch format %d for version: %s", format, version);
767 patch = xml_create_patchset_v1(source, target, config, FALSE);
770 patch = xml_create_patchset_v2(source, target);
773 crm_err(
"Unknown patch format: %d", format);
784 const char *version = NULL;
787 if (patch == NULL || source == NULL || target == NULL) {
796 if (format > 1 && with_digest == FALSE) {
810 __xml_log_element(
int log_level,
const char *file,
const char *
function,
int line,
811 const char *prefix, xmlNode *
data,
int depth,
int options);
817 xmlNode *child = NULL;
818 xmlNode *added = NULL;
819 xmlNode *removed = NULL;
820 gboolean is_first = TRUE;
822 int add[] = { 0, 0, 0 };
823 int del[] = { 0, 0, 0 };
825 const char *fmt = NULL;
826 const char *digest = NULL;
829 static struct qb_log_callsite *patchset_cs = NULL;
831 if (patchset_cs == NULL) {
832 patchset_cs = qb_log_callsite_get(
function, __FILE__,
"xml-patchset", log_level, __LINE__, 0);
835 if (patchset == NULL) {
839 }
else if (log_level == 0) {
849 if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
851 "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
853 "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
855 }
else if (patchset != NULL && (add[0] || add[1] || add[2])) {
857 "%s: Local-only Change: %d.%d.%d",
function ?
function :
"",
858 add[0], add[1], add[2]);
863 xmlNode *change = NULL;
865 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
870 }
else if(strcmp(op,
"create") == 0) {
871 int lpc = 0, max = 0;
874 max = strlen(prefix);
875 __xml_log_element(log_level, __FILE__,
function, __LINE__, prefix, change->children,
878 for(lpc = 2; lpc < max; lpc++) {
882 __xml_log_element(log_level, __FILE__,
function, __LINE__, prefix, change->children,
886 }
else if(strcmp(op,
"move") == 0) {
889 }
else if(strcmp(op,
"modify") == 0) {
898 for (child = __xml_first_child(clist); child != NULL; child = __xml_next(child)) {
903 }
else if(strcmp(op,
"set") == 0) {
909 o_set += snprintf(buffer_set + o_set,
XML_BUFFER_SIZE - o_set,
"@%s=%s", name, value);
911 }
else if(strcmp(op,
"unset") == 0) {
913 o_unset += snprintf(buffer_unset + o_unset,
XML_BUFFER_SIZE - o_unset,
", ");
915 o_unset += snprintf(buffer_unset + o_unset,
XML_BUFFER_SIZE - o_unset,
"@%s", name);
919 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"+ %s: %s", xpath, buffer_set);
922 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"-- %s: %s", xpath, buffer_unset);
925 }
else if(strcmp(op,
"delete") == 0) {
930 do_crm_log_alias(log_level, __FILE__,
function, __LINE__,
"-- %s (%d)", xpath, position);
940 if (log_level < LOG_DEBUG ||
function == NULL) {
945 for (child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
957 for (child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
977 doc = xml->doc->_private;
982 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
985 if (deleted_obj->position >= 0) {
987 deleted_obj->path, deleted_obj->position);
1002 xmlNode *top = NULL;
1009 crm_trace(
"Accepting changes to %p", xml);
1010 doc = xml->doc->_private;
1011 top = xmlDocGetRootElement(xml->doc);
1013 __xml_private_clean(xml->doc->_private);
1021 __xml_accept_changes(top);
1025 find_element(xmlNode *haystack, xmlNode *needle, gboolean exact)
1028 return (needle->type == XML_COMMENT_NODE)?
1029 find_xml_comment(haystack, needle, exact)
1030 :
find_entity(haystack, crm_element_name(needle),
ID(needle));
1035 __subtract_xml_object(xmlNode * target, xmlNode * patch)
1037 xmlNode *patch_child = NULL;
1038 xmlNode *cIter = NULL;
1039 xmlAttrPtr xIter = NULL;
1042 const char *name = NULL;
1043 const char *value = NULL;
1045 if (target == NULL || patch == NULL) {
1049 if (target->type == XML_COMMENT_NODE) {
1052 subtract_xml_comment(target->parent, target, patch, &dummy);
1055 name = crm_element_name(target);
1063 if (value != NULL && strcmp(value,
"removed:top") == 0) {
1064 crm_trace(
"We are the root of the deletion: %s.id=%s", name,
id);
1070 for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1071 const char *p_name = (
const char *)xIter->name;
1080 cIter = __xml_first_child(target);
1082 xmlNode *target_child = cIter;
1084 cIter = __xml_next(cIter);
1085 patch_child = find_element(patch, target_child, FALSE);
1086 __subtract_xml_object(target_child, patch_child);
1092 __add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * patch)
1094 xmlNode *patch_child = NULL;
1095 xmlNode *target_child = NULL;
1096 xmlAttrPtr xIter = NULL;
1098 const char *
id = NULL;
1099 const char *name = NULL;
1100 const char *value = NULL;
1102 if (patch == NULL) {
1104 }
else if (parent == NULL && target == NULL) {
1112 && strcmp(value,
"added:top") == 0) {
1114 name = crm_element_name(patch);
1115 crm_trace(
"We are the root of the addition: %s.id=%s", name,
id);
1119 }
else if(target == NULL) {
1121 name = crm_element_name(patch);
1122 crm_err(
"Could not locate: %s.id=%s", name,
id);
1126 if (target->type == XML_COMMENT_NODE) {
1127 add_xml_comment(parent, target, patch);
1130 name = crm_element_name(target);
1135 for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1136 const char *p_name = (
const char *)xIter->name;
1144 for (patch_child = __xml_first_child(patch); patch_child != NULL;
1145 patch_child = __xml_next(patch_child)) {
1147 target_child = find_element(target, patch_child, FALSE);
1148 __add_xml_object(target, target_child, patch_child);
1164 find_patch_xml_node(xmlNode *patchset,
int format,
bool added,
1165 xmlNode **patch_node)
1172 label = added?
"diff-added" :
"diff-removed";
1175 if (cib_node != NULL) {
1176 *patch_node = cib_node;
1180 label = added?
"target" :
"source";
1185 crm_warn(
"Unknown patch format: %d", format);
1196 xmlNode *tmp = NULL;
1198 const char *vfields[] = {
1208 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
1212 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1214 crm_trace(
"Got %d for del[%s]", del[lpc], vfields[lpc]);
1219 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
1223 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1225 crm_trace(
"Got %d for add[%s]", add[lpc], vfields[lpc]);
1233 xml_patch_version_check(xmlNode *xml, xmlNode *patchset,
int format)
1236 bool changed = FALSE;
1238 int this[] = { 0, 0, 0 };
1239 int add[] = { 0, 0, 0 };
1240 int del[] = { 0, 0, 0 };
1242 const char *vfields[] = {
1248 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1250 crm_trace(
"Got %d for this[%s]",
this[lpc], vfields[lpc]);
1251 if (
this[lpc] < 0) {
1259 add[2] =
this[2] + 1;
1260 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1261 del[lpc] =
this[lpc];
1266 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1267 if(
this[lpc] < del[lpc]) {
1268 crm_debug(
"Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)", vfields[lpc],
1269 this[0],
this[1],
this[2], del[0], del[1], del[2], add[0], add[1], add[2]);
1272 }
else if(
this[lpc] > del[lpc]) {
1273 crm_info(
"Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p", vfields[lpc],
1274 this[0],
this[1],
this[2], del[0], del[1], del[2], add[0], add[1], add[2], patchset);
1280 for(lpc = 0; lpc <
DIMOF(vfields); lpc++) {
1281 if(add[lpc] > del[lpc]) {
1286 if(changed == FALSE) {
1287 crm_notice(
"Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
1291 crm_debug(
"Can apply patch %d.%d.%d to %d.%d.%d",
1292 add[0], add[1], add[2],
this[0],
this[1],
this[2]);
1297 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset)
1300 int root_nodes_seen = 0;
1302 xmlNode *child_diff = NULL;
1303 xmlNode *added =
find_xml_node(patchset,
"diff-added", FALSE);
1304 xmlNode *removed =
find_xml_node(patchset,
"diff-removed", FALSE);
1308 for (child_diff = __xml_first_child(removed); child_diff != NULL;
1309 child_diff = __xml_next(child_diff)) {
1310 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1311 if (root_nodes_seen == 0) {
1312 __subtract_xml_object(xml, child_diff);
1317 if (root_nodes_seen > 1) {
1318 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1322 root_nodes_seen = 0;
1325 xmlNode *child_diff = NULL;
1327 for (child_diff = __xml_first_child(added); child_diff != NULL;
1328 child_diff = __xml_next(child_diff)) {
1329 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1330 if (root_nodes_seen == 0) {
1331 __add_xml_object(NULL, xml, child_diff);
1337 if (root_nodes_seen > 1) {
1338 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1349 __first_xml_child_match(xmlNode *parent,
const char *name,
const char *
id,
int position)
1351 xmlNode *cIter = NULL;
1353 for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
1354 if(strcmp((
const char *)cIter->name, name) != 0) {
1357 const char *cid =
ID(cIter);
1358 if(cid == NULL || strcmp(cid,
id) != 0) {
1364 if (cIter->type == XML_COMMENT_NODE
1366 && __xml_offset(cIter) != position) {
1389 __xml_find_path(xmlNode *top,
const char *key,
int target_position)
1391 xmlNode *target = (xmlNode*) top->doc;
1392 const char *current = key;
1402 key_len = strlen(key);
1408 remainder = calloc(key_len,
sizeof(
char));
1411 section = calloc(key_len,
sizeof(
char));
1414 id = calloc(key_len,
sizeof(
char));
1417 tag = calloc(key_len,
sizeof(
char));
1422 rc = sscanf(current,
"/%[^/]%s", section, remainder);
1425 int f = sscanf(section,
"%[^[][@id='%[^']", tag,
id);
1426 int current_position = -1;
1431 if ((rc == 1) && (target_position >= 0)) {
1432 current_position = target_position;
1437 target = __first_xml_child_match(target, tag, NULL, current_position);
1440 target = __first_xml_child_match(target, tag,
id, current_position);
1447 current = remainder;
1451 }
while ((rc == 2) && target);
1455 (path = (
char *) xmlGetNodePath(target)), key);
1469 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
1472 xmlNode *change = NULL;
1473 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
1474 xmlNode *match = NULL;
1479 crm_trace(
"Processing %s %s", change->name, op);
1484 if(strcmp(op,
"delete") == 0) {
1487 match = __xml_find_path(xml, xpath, position);
1488 crm_trace(
"Performing %s on %s with %p", op, xpath, match);
1490 if(match == NULL && strcmp(op,
"delete") == 0) {
1491 crm_debug(
"No %s match for %s in %p", op, xpath, xml->doc);
1494 }
else if(match == NULL) {
1495 crm_err(
"No %s match for %s in %p", op, xpath, xml->doc);
1499 }
else if(strcmp(op,
"create") == 0) {
1501 xmlNode *child = NULL;
1502 xmlNode *match_child = NULL;
1504 match_child = match->children;
1507 while(match_child && position != __xml_offset(match_child)) {
1508 match_child = match_child->next;
1511 child = xmlDocCopyNode(change->children, match->doc, 1);
1513 crm_trace(
"Adding %s at position %d", child->name, position);
1514 xmlAddPrevSibling(match_child, child);
1516 }
else if(match->last) {
1517 crm_trace(
"Adding %s at position %d (end)", child->name, position);
1518 xmlAddNextSibling(match->last, child);
1521 crm_trace(
"Adding %s at position %d (first)", child->name, position);
1523 xmlAddChild(match, child);
1525 crm_node_created(child);
1527 }
else if(strcmp(op,
"move") == 0) {
1531 if(position != __xml_offset(match)) {
1532 xmlNode *match_child = NULL;
1535 if(p > __xml_offset(match)) {
1540 match_child = match->parent->children;
1542 while(match_child && p != __xml_offset(match_child)) {
1543 match_child = match_child->next;
1546 crm_trace(
"Moving %s to position %d (was %d, prev %p, %s %p)",
1547 match->name, position, __xml_offset(match), match->prev,
1548 match_child?
"next":
"last", match_child?match_child:match->parent->last);
1551 xmlAddPrevSibling(match_child, match);
1555 xmlAddNextSibling(match->parent->last, match);
1559 crm_trace(
"%s is already in position %d", match->name, position);
1562 if(position != __xml_offset(match)) {
1563 crm_err(
"Moved %s.%s to position %d instead of %d (%p)",
1564 match->name,
ID(match), __xml_offset(match), position, match->prev);
1568 }
else if(strcmp(op,
"delete") == 0) {
1571 }
else if(strcmp(op,
"modify") == 0) {
1572 xmlAttr *pIter = pcmk__first_xml_attr(match);
1579 while(pIter != NULL) {
1580 const char *name = (
const char *)pIter->name;
1582 pIter = pIter->next;
1586 for (pIter = pcmk__first_xml_attr(attrs); pIter != NULL; pIter = pIter->next) {
1587 const char *name = (
const char *)pIter->name;
1594 crm_err(
"Unknown operation: %s", op);
1605 xmlNode *old = NULL;
1608 if(patchset == NULL) {
1616 rc = xml_patch_version_check(xml, patchset, format);
1630 rc = xml_apply_patchset_v1(xml, patchset);
1633 rc = xml_apply_patchset_v2(xml, patchset);
1636 crm_err(
"Unknown patch format: %d", format);
1642 static struct qb_log_callsite *digest_cs = NULL;
1644 char *new_digest = NULL;
1647 if (digest_cs == NULL) {
1649 qb_log_callsite_get(__func__, __FILE__,
"diff-digest",
LOG_TRACE, __LINE__,
1655 crm_info(
"v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
1658 if (digest_cs && digest_cs->targets) {
1664 crm_trace(
"%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
1668 crm_trace(
"v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
1680 xmlNode *a_child = NULL;
1681 const char *name =
"NULL";
1684 name = crm_element_name(root);
1687 if (search_path == NULL) {
1688 crm_warn(
"Will never find <NULL>");
1692 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
1693 if (strcmp((
const char *)a_child->name, search_path) == 0) {
1700 crm_warn(
"Could not find %s in %s.", search_path, name);
1701 }
else if (root != NULL) {
1702 crm_trace(
"Could not find %s in %s.", search_path, name);
1704 crm_trace(
"Could not find %s in <NULL>.", search_path);
1714 find_entity_by_attr_or_just_name(xmlNode *parent,
const char *node_name,
1715 const char *attr_n,
const char *attr_v)
1720 CRM_CHECK(attr_n == NULL || attr_v != NULL,
return NULL);
1722 for (child = __xml_first_child(parent); child != NULL; child = __xml_next(child)) {
1724 if (node_name == NULL || !strcmp((
const char *) child->name, node_name)) {
1734 attr_n ? attr_n :
"",
1736 attr_n ? attr_v :
"",
1737 crm_element_name(parent));
1745 return find_entity_by_attr_or_just_name(parent, node_name,
1753 crm_warn(
"No node to copy properties from");
1755 }
else if (target == NULL) {
1756 crm_err(
"No node to copy properties into");
1759 xmlAttrPtr pIter = NULL;
1761 for (pIter = pcmk__first_xml_attr(src); pIter != NULL; pIter = pIter->next) {
1762 const char *p_name = (
const char *)pIter->name;
1763 const char *p_value = pcmk__xml_attr_value(pIter);
1776 xmlNode *child = NULL;
1777 xmlAttrPtr pIter = NULL;
1779 for (pIter = pcmk__first_xml_attr(target); pIter != NULL; pIter = pIter->next) {
1780 const char *p_name = (
const char *)pIter->name;
1781 const char *p_value = pcmk__xml_attr_value(pIter);
1785 for (child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
1798 const char *old_value = NULL;
1800 if (value == NULL || name == NULL) {
1806 if (old_value == NULL) {
1808 goto set_unexpanded;
1810 }
else if (strstr(value, name) != value) {
1811 goto set_unexpanded;
1814 name_len = strlen(name);
1815 value_len = strlen(value);
1816 if (value_len < (name_len + 2)
1817 || value[name_len] !=
'+' || (value[name_len + 1] !=
'+' && value[name_len + 1] !=
'=')) {
1818 goto set_unexpanded;
1824 if (old_value != value) {
1828 if (value[name_len + 1] !=
'+') {
1829 const char *offset_s = value + (name_len + 2);
1833 int_value += offset;
1843 if (old_value == value) {
1861 xmlDocSetRootElement(doc, node);
1862 xmlSetTreeDoc(node, doc);
1870 xmlNode *child = NULL;
1873 CRM_CHECK(src_node != NULL,
return NULL);
1875 child = xmlDocCopyNode(src_node, doc, 1);
1876 xmlAddChild(parent, child);
1877 crm_node_created(child);
1893 xmlNode *node = NULL;
1895 if (name == NULL || name[0] == 0) {
1896 CRM_CHECK(name != NULL && name[0] == 0,
return NULL);
1900 if (parent == NULL) {
1902 node = xmlNewDocRawNode(doc, NULL, (
pcmkXmlStr) name, NULL);
1903 xmlDocSetRootElement(doc, node);
1907 node = xmlNewDocRawNode(doc, NULL, (
pcmkXmlStr) name, NULL);
1908 xmlAddChild(parent, node);
1910 crm_node_created(node);
1916 int offset,
size_t buffer_size)
1918 const char *
id =
ID(xml);
1920 if(offset == 0 && prefix == NULL && xml->parent) {
1926 offset += snprintf(buffer + offset, buffer_size - offset,
1927 "/%s[@id='%s']", (
const char *) xml->name,
id);
1928 }
else if(xml->name) {
1929 offset += snprintf(buffer + offset, buffer_size - offset,
1930 "/%s", (
const char *) xml->name);
1943 return strdup(buffer);
1949 free_xml_with_position(xmlNode * child,
int position)
1951 if (child != NULL) {
1952 xmlNode *top = NULL;
1953 xmlDoc *doc = child->doc;
1957 top = xmlDocGetRootElement(doc);
1960 if (doc != NULL && top == child) {
1979 sizeof(buffer)) > 0) {
1982 crm_trace(
"Deleting %s %p from %p", buffer, child, doc);
1984 deleted_obj->path = strdup(buffer);
1986 deleted_obj->position = -1;
1988 if (child->type == XML_COMMENT_NODE) {
1989 if (position >= 0) {
1990 deleted_obj->position = position;
1993 deleted_obj->position = __xml_offset(child);
2006 xmlUnlinkNode(child);
2016 free_xml_with_position(child, -1);
2023 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
2025 xmlDocSetRootElement(doc, copy);
2026 xmlSetTreeDoc(copy, doc);
2031 crm_xml_err(
void *ctx,
const char *fmt, ...)
2032 G_GNUC_PRINTF(2, 3);
2035 crm_xml_err(
void *ctx, const
char *fmt, ...)
2038 static struct qb_log_callsite *xml_error_cs = NULL;
2040 if (xml_error_cs == NULL) {
2041 xml_error_cs = qb_log_callsite_get(
2046 if (xml_error_cs && xml_error_cs->targets) {
2048 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__,
"xml library error",
2050 "XML Error: ", fmt, ap);
2060 xmlNode *xml = NULL;
2061 xmlDocPtr output = NULL;
2062 xmlParserCtxtPtr ctxt = NULL;
2063 xmlErrorPtr last_error = NULL;
2065 if (input == NULL) {
2066 crm_err(
"Can't parse NULL input");
2071 ctxt = xmlNewParserCtxt();
2076 xmlCtxtResetLastError(ctxt);
2077 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2080 xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
2081 XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
2083 xml = xmlDocGetRootElement(output);
2085 last_error = xmlCtxtGetLastError(ctxt);
2086 if (last_error && last_error->code != XML_ERR_OK) {
2092 crm_warn(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
2093 last_error->domain, last_error->level, last_error->code, last_error->message);
2095 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
2098 }
else if (last_error->code != XML_ERR_DOCUMENT_END) {
2099 crm_err(
"Couldn't%s parse %d chars: %s", xml ?
" fully" :
"", (
int)strlen(input),
2106 int len = strlen(input);
2110 crm_warn(
"Parse error[+%.3d]: %.80s", lpc, input+lpc);
2118 xmlFreeParserCtxt(ctxt);
2125 size_t data_length = 0;
2126 size_t read_chars = 0;
2128 char *xml_buffer = NULL;
2129 xmlNode *xml_obj = NULL;
2133 read_chars = fread(xml_buffer + data_length, 1,
XML_BUFFER_SIZE, stdin);
2134 data_length += read_chars;
2137 if (data_length == 0) {
2138 crm_warn(
"No XML supplied on stdin");
2143 xml_buffer[data_length] =
'\0';
2152 decompress_file(
const char *filename)
2154 char *buffer = NULL;
2158 size_t length = 0, read_len = 0;
2160 BZFILE *bz_file = NULL;
2161 FILE *input = fopen(filename,
"r");
2163 if (input == NULL) {
2164 crm_perror(LOG_ERR,
"Could not open %s for reading", filename);
2168 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
2170 crm_err(
"Could not prepare to read compressed %s: %s "
2172 BZ2_bzReadClose(&rc, bz_file);
2177 while (rc == BZ_OK) {
2179 read_len = BZ2_bzRead(&rc, bz_file, buffer + length,
XML_BUFFER_SIZE);
2181 crm_trace(
"Read %ld bytes from file: %d", (
long)read_len, rc);
2183 if (rc == BZ_OK || rc == BZ_STREAM_END) {
2188 buffer[length] =
'\0';
2190 if (rc != BZ_STREAM_END) {
2191 crm_err(
"Could not read compressed %s: %s "
2197 BZ2_bzReadClose(&rc, bz_file);
2201 crm_err(
"Could not read compressed %s: not built with bzlib support",
2210 xmlNode *iter = xml->children;
2213 xmlNode *next = iter->next;
2215 switch (iter->type) {
2218 xmlUnlinkNode(iter);
2222 case XML_ELEMENT_NODE:
2239 xmlNode *xml = NULL;
2240 xmlDocPtr output = NULL;
2241 gboolean uncompressed = TRUE;
2242 xmlParserCtxtPtr ctxt = NULL;
2243 xmlErrorPtr last_error = NULL;
2244 static int xml_options = XML_PARSE_NOBLANKS | XML_PARSE_RECOVER;
2247 ctxt = xmlNewParserCtxt();
2252 xmlCtxtResetLastError(ctxt);
2253 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2260 if (filename == NULL) {
2262 output = xmlCtxtReadFd(ctxt, STDIN_FILENO,
"unknown.xml", NULL, xml_options);
2264 }
else if (uncompressed) {
2265 output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options);
2268 char *input = decompress_file(filename);
2270 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
2275 if (output && (xml = xmlDocGetRootElement(output))) {
2279 last_error = xmlCtxtGetLastError(ctxt);
2280 if (last_error && last_error->code != XML_ERR_OK) {
2286 crm_err(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
2287 last_error->domain, last_error->level, last_error->code, last_error->message);
2289 if (last_error && last_error->code != XML_ERR_OK) {
2290 crm_err(
"Couldn't%s parse %s", xml ?
" fully" :
"", filename);
2297 xmlFreeParserCtxt(ctxt);
2312 time_t now = time(NULL);
2313 char *now_str = ctime(&now);
2329 for (c =
id; *c; ++c) {
2354 va_start(ap, format);
2355 len = vasprintf(&
id, format, ap);
2376 write_xml_stream(xmlNode * xml_node,
const char *filename, FILE * stream, gboolean compress)
2379 char *buffer = NULL;
2380 unsigned int out = 0;
2393 unsigned int in = 0;
2394 BZFILE *bz_file = NULL;
2396 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
2398 crm_warn(
"Not compressing %s: could not prepare file stream: %s "
2401 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
2403 crm_warn(
"Not compressing %s: could not compress data: %s "
2404 CRM_XS " bzerror=%d errno=%d",
2410 BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
2412 crm_warn(
"Not compressing %s: could not write compressed data: %s "
2413 CRM_XS " bzerror=%d errno=%d",
2418 crm_trace(
"Compressed XML for %s from %u bytes to %u",
2423 crm_warn(
"Not compressing %s: not built with bzlib support", filename);
2428 res = fprintf(stream,
"%s", buffer);
2438 if (fflush(stream) != 0) {
2440 crm_perror(LOG_ERR,
"flushing %s", filename);
2444 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
2446 crm_perror(LOG_ERR,
"synchronizing %s", filename);
2451 crm_trace(
"Saved %d bytes%s to %s as XML",
2452 res, ((out > 0)?
" (compressed)" :
""), filename);
2469 write_xml_fd(xmlNode * xml_node,
const char *filename,
int fd, gboolean compress)
2471 FILE *stream = NULL;
2473 CRM_CHECK(xml_node && (fd > 0),
return -EINVAL);
2474 stream = fdopen(fd,
"w");
2475 if (stream == NULL) {
2478 return write_xml_stream(xml_node, filename, stream, compress);
2493 FILE *stream = NULL;
2495 CRM_CHECK(xml_node && filename,
return -EINVAL);
2496 stream = fopen(filename,
"w");
2497 if (stream == NULL) {
2500 return write_xml_stream(xml_node, filename, stream, compress);
2508 return __xml_first_child(tmp);
2521 crm_xml_escape_shuffle(
char *text,
int start,
int *length,
const char *replace)
2524 int offset = strlen(replace) - 1;
2527 text = realloc_safe(text, *length);
2529 for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
2530 text[lpc] = text[lpc - offset];
2533 memcpy(text + start, replace, offset + 1);
2542 int length = 1 + strlen(text);
2543 char *copy = strdup(text);
2560 for (index = 0; index < length; index++) {
2561 switch (copy[index]) {
2565 copy = crm_xml_escape_shuffle(copy, index, &length,
"<");
2569 copy = crm_xml_escape_shuffle(copy, index, &length,
">");
2573 copy = crm_xml_escape_shuffle(copy, index, &length,
""");
2577 copy = crm_xml_escape_shuffle(copy, index, &length,
"'");
2581 copy = crm_xml_escape_shuffle(copy, index, &length,
"&");
2586 copy = crm_xml_escape_shuffle(copy, index, &length,
" ");
2591 copy = crm_xml_escape_shuffle(copy, index, &length,
"\\n");
2595 copy = crm_xml_escape_shuffle(copy, index, &length,
"\\r");
2605 if(copy[index] <
' ' || copy[index] >
'~') {
2609 copy = crm_xml_escape_shuffle(copy, index, &length, replace);
2623 dump_xml_attr(xmlAttrPtr attr,
int options,
char **buffer,
int *offset,
int *max)
2625 char *p_value = NULL;
2626 const char *p_name = NULL;
2630 if (attr == NULL || attr->children == NULL) {
2639 p_name = (
const char *)attr->name;
2641 buffer_print(*buffer, *max, *offset,
" %s=\"%s\"", p_name, p_value);
2646 __xml_log_element(
int log_level,
const char *file,
const char *
function,
int line,
2647 const char *prefix, xmlNode *
data,
int depth,
int options)
2651 const char *name = NULL;
2652 const char *hidden = NULL;
2654 xmlNode *child = NULL;
2655 xmlAttrPtr pIter = NULL;
2661 name = crm_element_name(data);
2664 char *buffer = NULL;
2666 insert_prefix(options, &buffer, &offset, &max, depth);
2668 if (data->type == XML_COMMENT_NODE) {
2669 buffer_print(buffer, max, offset,
"<!--%s-->", data->content);
2675 for (pIter = pcmk__first_xml_attr(data); pIter != NULL; pIter = pIter->next) {
2677 const char *p_name = (
const char *)pIter->name;
2678 const char *p_value = pcmk__xml_attr_value(pIter);
2679 char *p_copy = NULL;
2688 }
else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
2689 p_copy = strdup(
"*****");
2695 buffer_print(buffer, max, offset,
" %s=\"%s\"", p_name, p_copy);
2710 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
2714 if(data->type == XML_COMMENT_NODE) {
2724 for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
2730 char *buffer = NULL;
2732 insert_prefix(options, &buffer, &offset, &max, depth);
2735 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
2741 __xml_log_change_element(
int log_level,
const char *file,
const char *
function,
int line,
2742 const char *prefix, xmlNode * data,
int depth,
int options)
2745 char *prefix_m = NULL;
2746 xmlNode *child = NULL;
2747 xmlAttrPtr pIter = NULL;
2755 prefix_m = strdup(prefix);
2760 __xml_log_element(log_level, file,
function, line,
2764 char *spaces = calloc(80, 1);
2765 int s_count = 0, s_max = 80;
2766 char *prefix_del = NULL;
2767 char *prefix_moved = NULL;
2768 const char *
flags = prefix;
2770 insert_prefix(options, &spaces, &s_count, &s_max, depth);
2771 prefix_del = strdup(prefix);
2772 prefix_del[0] =
'-';
2773 prefix_del[1] =
'-';
2774 prefix_moved = strdup(prefix);
2775 prefix_moved[1] =
'~';
2778 flags = prefix_moved;
2783 __xml_log_element(log_level, file,
function, line,
2786 for (pIter = pcmk__first_xml_attr(data); pIter != NULL; pIter = pIter->next) {
2787 const char *aname = (
const char*)pIter->name;
2789 p = pIter->_private;
2794 "%s %s @%s=%s", flags, spaces, aname, value);
2806 flags = prefix_moved;
2812 "%s %s @%s=%s", flags, spaces, aname, value);
2819 for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
2820 __xml_log_change_element(log_level, file,
function, line, prefix, child, depth + 1, options);
2823 __xml_log_element(log_level, file,
function, line,
2827 for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
2828 __xml_log_change_element(log_level, file,
function, line, prefix, child, depth + 1, options);
2838 const char *prefix, xmlNode * data,
int depth,
int options)
2840 xmlNode *a_child = NULL;
2842 char *prefix_m = NULL;
2844 if (prefix == NULL) {
2851 "No data to dump as XML");
2856 __xml_log_change_element(log_level, file,
function, line, prefix, data, depth, options);
2864 prefix_m = strdup(prefix);
2871 prefix_m = strdup(prefix);
2880 for (a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
2881 log_data_element(log_level, file,
function, line, prefix, a_child, depth + 1, options);
2884 __xml_log_element(log_level, file,
function, line, prefix, data, depth,
2891 dump_filtered_xml(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max)
2894 xmlAttrPtr xIter = NULL;
2895 static int filter_len =
DIMOF(filter);
2897 for (lpc = 0; options && lpc < filter_len; lpc++) {
2898 filter[lpc].found = FALSE;
2901 for (xIter = pcmk__first_xml_attr(data); xIter != NULL; xIter = xIter->next) {
2903 const char *p_name = (
const char *)xIter->name;
2905 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
2906 if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].
string) == 0) {
2907 filter[lpc].found = TRUE;
2913 if (skip == FALSE) {
2914 dump_xml_attr(xIter, options, buffer, offset, max);
2920 dump_xml_element(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
2922 const char *name = NULL;
2933 if (*buffer == NULL) {
2938 name = crm_element_name(data);
2941 insert_prefix(options, buffer, offset, max, depth);
2945 dump_filtered_xml(data, options, buffer, offset, max);
2948 xmlAttrPtr xIter = NULL;
2950 for (xIter = pcmk__first_xml_attr(data); xIter != NULL; xIter = xIter->next) {
2951 dump_xml_attr(xIter, options, buffer, offset, max);
2955 if (data->children == NULL) {
2966 if (data->children) {
2967 xmlNode *xChild = NULL;
2968 for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
2969 crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
2972 insert_prefix(options, buffer, offset, max, depth);
2975 if (options & xml_log_option_formatted) {
2982 dump_xml_text(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
2993 if (*buffer == NULL) {
2998 insert_prefix(options, buffer, offset, max, depth);
3000 buffer_print(*buffer, *max, *offset,
"%s", data->content);
3002 if (options & xml_log_option_formatted) {
3008 dump_xml_cdata(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3019 if (*buffer == NULL) {
3024 insert_prefix(options, buffer, offset, max, depth);
3027 buffer_print(*buffer, *max, *offset,
"%s", data->content);
3030 if (options & xml_log_option_formatted) {
3037 dump_xml_comment(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3048 if (*buffer == NULL) {
3053 insert_prefix(options, buffer, offset, max, depth);
3056 buffer_print(*buffer, *max, *offset,
"%s", data->content);
3059 if (options & xml_log_option_formatted) {
3065 crm_xml_dump(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
3073 if (is_not_set(options, xml_log_option_filtered)) {
3088 xmlBuffer *xml_buffer = NULL;
3096 xml_buffer = xmlBufferCreate();
3107 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_DOUBLEIT);
3109 *max = xmlNodeDump(xml_buffer, doc, data, 0, (options & xml_log_option_formatted));
3111 *buffer = strdup((
char *)xml_buffer->content);
3115 if ((now + 1) < next) {
3117 crm_err(
"xmlNodeDump() -> %dbytes took %ds", *max, next - now);
3120 xmlBufferFree(xml_buffer);
3125 switch(data->type) {
3126 case XML_ELEMENT_NODE:
3128 dump_xml_element(data, options, buffer, offset, max, depth);
3133 dump_xml_text(data, options, buffer, offset, max, depth);
3136 case XML_COMMENT_NODE:
3137 dump_xml_comment(data, options, buffer, offset, max, depth);
3139 case XML_CDATA_SECTION_NODE:
3140 dump_xml_cdata(data, options, buffer, offset, max, depth);
3143 crm_warn(
"Unhandled type: %d", data->type);
3178 char *buffer = NULL;
3179 int offset = 0, max = 0;
3188 char *buffer = NULL;
3189 int offset = 0, max = 0;
3191 crm_xml_dump(an_xml_node, xml_log_option_formatted, &buffer, &offset, &max, 0);
3198 char *buffer = NULL;
3199 int offset = 0, max = 0;
3201 crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
3208 if (xml_root != NULL && xml_root->children != NULL) {
3218 crm_trace(
"Cannot remove %s from %s", name, obj->name);
3223 xmlAttr *attr = xmlHasProp(obj, (
pcmkXmlStr) name);
3238 xmlNode *child = NULL;
3243 for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
3253 if (filename == NULL) {
3261 crm_info(
"Saving %s to %s", desc, filename);
3269 gboolean result = TRUE;
3270 int root_nodes_seen = 0;
3271 static struct qb_log_callsite *digest_cs = NULL;
3275 xmlNode *child_diff = NULL;
3277 xmlNode *removed =
find_xml_node(diff,
"diff-removed", FALSE);
3279 CRM_CHECK(new_xml != NULL,
return FALSE);
3280 if (digest_cs == NULL) {
3282 qb_log_callsite_get(__func__, __FILE__,
"diff-digest",
LOG_TRACE, __LINE__,
3287 for (child_diff = __xml_first_child(removed); child_diff != NULL;
3288 child_diff = __xml_next(child_diff)) {
3289 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3290 if (root_nodes_seen == 0) {
3296 if (root_nodes_seen == 0) {
3299 }
else if (root_nodes_seen > 1) {
3300 crm_err(
"(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3304 root_nodes_seen = 0;
3307 xmlNode *child_diff = NULL;
3309 for (child_diff = __xml_first_child(added); child_diff != NULL;
3310 child_diff = __xml_next(child_diff)) {
3311 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3312 if (root_nodes_seen == 0) {
3313 add_xml_object(NULL, *new_xml, child_diff, TRUE);
3319 if (root_nodes_seen > 1) {
3320 crm_err(
"(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3323 }
else if (result && digest) {
3324 char *new_digest = NULL;
3329 crm_info(
"Digest mis-match: expected %s, calculated %s", digest, new_digest);
3332 crm_trace(
"%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
3333 if (digest_cs && digest_cs->targets) {
3340 crm_trace(
"Digest matched: expected %s, calculated %s", digest, new_digest);
3344 }
else if (result) {
3352 __xml_diff_object(xmlNode *old_xml, xmlNode *new_xml)
3354 xmlNode *cIter = NULL;
3355 xmlAttr *pIter = NULL;
3358 if (old_xml == NULL) {
3359 crm_node_created(new_xml);
3373 for (pIter = pcmk__first_xml_attr(new_xml); pIter != NULL; pIter = pIter->next) {
3380 for (pIter = pcmk__first_xml_attr(old_xml); pIter != NULL; ) {
3381 xmlAttr *prop = pIter;
3383 const char *name = (
const char *)pIter->name;
3385 xmlAttr *exists = xmlHasProp(new_xml, pIter->name);
3387 pIter = pIter->next;
3388 if(exists == NULL) {
3389 p = new_xml->doc->_private;
3393 exists = xmlSetProp(new_xml, (
pcmkXmlStr) name,
3397 p = exists->_private;
3400 crm_trace(
"Lost %s@%s=%s", old_xml->name, name, old_value);
3404 int p_new = __xml_offset((xmlNode*)exists);
3405 int p_old = __xml_offset((xmlNode*)prop);
3408 p = exists->_private;
3411 if(strcmp(value, old_value) != 0) {
3418 old_xml->name, name, old_value, vcopy);
3419 xmlSetProp(new_xml, prop->name, (
pcmkXmlStr) old_value);
3423 }
else if ((p_old != p_new)
3426 old_xml->name, name, p_old, p_new);
3427 __xml_node_dirty(new_xml);
3435 p = exists->_private;
3442 for (pIter = pcmk__first_xml_attr(new_xml); pIter != NULL; ) {
3443 xmlAttr *prop = pIter;
3446 pIter = pIter->next;
3448 char *name = strdup((
const char *)prop->name);
3451 crm_trace(
"Created %s@%s=%s", new_xml->name, name, value);
3456 xmlUnsetProp(new_xml, prop->name);
3464 for (cIter = __xml_first_child(old_xml); cIter != NULL; ) {
3465 xmlNode *old_child = cIter;
3466 xmlNode *new_child = find_element(new_xml, cIter, TRUE);
3468 cIter = __xml_next(cIter);
3470 __xml_diff_object(old_child, new_child);
3475 xmlNode *top = xmlDocGetRootElement(candidate->doc);
3477 __xml_node_clean(candidate);
3480 free_xml_with_position(candidate, __xml_offset(old_child));
3482 if (find_element(new_xml, old_child, TRUE) == NULL) {
3490 for (cIter = __xml_first_child(new_xml); cIter != NULL; ) {
3491 xmlNode *new_child = cIter;
3492 xmlNode *old_child = find_element(old_xml, cIter, TRUE);
3494 cIter = __xml_next(cIter);
3495 if(old_child == NULL) {
3498 __xml_diff_object(old_child, new_child);
3502 int p_new = __xml_offset(new_child);
3503 int p_old = __xml_offset(old_child);
3505 if(p_old != p_new) {
3508 crm_info(
"%s.%s moved from %d to %d",
3509 new_child->name,
ID(new_child), p_old, p_new);
3510 __xml_node_dirty(new_xml);
3514 p = old_child->_private;
3516 p = new_child->_private;
3535 crm_element_name(new_xml)),
3543 __xml_diff_object(old_xml, new_xml);
3549 xmlNode *tmp1 = NULL;
3566 if (added->children == NULL && removed->children == NULL) {
3577 xmlNode *cIter = NULL;
3578 xmlAttrPtr pIter = NULL;
3579 gboolean can_prune = TRUE;
3580 const char *name = crm_element_name(xml_node);
3589 for (pIter = pcmk__first_xml_attr(xml_node); pIter != NULL; pIter = pIter->next) {
3590 const char *p_name = (
const char *)pIter->name;
3598 cIter = __xml_first_child(xml_node);
3600 xmlNode *child = cIter;
3602 cIter = __xml_next(cIter);
3613 find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact)
3615 xmlNode *a_child = NULL;
3616 int search_offset = __xml_offset(search_comment);
3618 CRM_CHECK(search_comment->type == XML_COMMENT_NODE,
return NULL);
3620 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
3622 int offset = __xml_offset(a_child);
3625 if (offset < search_offset) {
3628 }
else if (offset > search_offset) {
3637 if (a_child->type == XML_COMMENT_NODE
3638 &&
safe_str_eq((
const char *)a_child->content, (
const char *)search_comment->content)) {
3650 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
3654 CRM_CHECK(left->type == XML_COMMENT_NODE,
return NULL);
3657 ||
safe_str_neq((
const char *)left->content, (
const char *)right->content)) {
3658 xmlNode *deleted = NULL;
3671 gboolean full, gboolean * changed,
const char *marker)
3673 gboolean dummy = FALSE;
3674 gboolean skip = FALSE;
3675 xmlNode *diff = NULL;
3676 xmlNode *right_child = NULL;
3677 xmlNode *left_child = NULL;
3678 xmlAttrPtr xIter = NULL;
3680 const char *
id = NULL;
3681 const char *name = NULL;
3682 const char *value = NULL;
3683 const char *right_val = NULL;
3686 static int filter_len =
DIMOF(filter);
3688 if (changed == NULL) {
3696 if (left->type == XML_COMMENT_NODE) {
3697 return subtract_xml_comment(parent, left, right, changed);
3701 if (right == NULL) {
3702 xmlNode *deleted = NULL;
3704 crm_trace(
"Processing <%s id=%s> (complete copy)", crm_element_name(left),
id);
3712 name = crm_element_name(left);
3718 if (value != NULL && strcmp(value,
"removed:top") == 0) {
3719 crm_trace(
"We are the root of the deletion: %s.id=%s", name,
id);
3728 for (lpc = 0; lpc < filter_len; lpc++) {
3729 filter[lpc].found = FALSE;
3733 for (left_child = __xml_first_child(left); left_child != NULL;
3734 left_child = __xml_next(left_child)) {
3735 gboolean child_changed = FALSE;
3737 right_child = find_element(right, left_child, FALSE);
3739 if (child_changed) {
3744 if (*changed == FALSE) {
3748 xmlAttrPtr pIter = NULL;
3750 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3751 const char *p_name = (
const char *)pIter->name;
3752 const char *p_value = pcmk__xml_attr_value(pIter);
3762 for (xIter = pcmk__first_xml_attr(left); xIter != NULL; xIter = xIter->next) {
3763 const char *prop_name = (
const char *)xIter->name;
3764 xmlAttrPtr right_attr = NULL;
3774 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
3775 if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].
string) == 0) {
3776 filter[lpc].found = TRUE;
3786 right_attr = xmlHasProp(right, (
pcmkXmlStr) prop_name);
3788 p = right_attr->_private;
3796 xmlAttrPtr pIter = NULL;
3798 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3799 const char *p_name = (
const char *)pIter->name;
3800 const char *p_value = pcmk__xml_attr_value(pIter);
3817 if (strcmp(left_value, right_val) == 0) {
3823 xmlAttrPtr pIter = NULL;
3825 crm_trace(
"Changes detected to %s in <%s id=%s>", prop_name,
3826 crm_element_name(left),
id);
3827 for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3828 const char *p_name = (
const char *)pIter->name;
3829 const char *p_value = pcmk__xml_attr_value(pIter);
3836 crm_trace(
"Changes detected to %s (%s -> %s) in <%s id=%s>",
3837 prop_name, left_value, right_val, crm_element_name(left),
id);
3844 if (*changed == FALSE) {
3848 }
else if (full == FALSE &&
id) {
3856 add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update)
3859 CRM_CHECK(update->type == XML_COMMENT_NODE,
return 0);
3861 if (target == NULL) {
3862 target = find_xml_comment(parent, update, FALSE);
3865 if (target == NULL) {
3869 }
else if (
safe_str_neq((
const char *)target->content, (
const char *)update->content)) {
3870 xmlFree(target->content);
3871 target->content = xmlStrdup(update->content);
3878 add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff)
3880 xmlNode *a_child = NULL;
3881 const char *object_name = NULL,
3882 *object_href = NULL,
3883 *object_href_val = NULL;
3892 if (update->type == XML_COMMENT_NODE) {
3893 return add_xml_comment(parent, target, update);
3896 object_name = crm_element_name(update);
3897 object_href_val =
ID(update);
3898 if (object_href_val != NULL) {
3905 CRM_CHECK(object_name != NULL,
return 0);
3906 CRM_CHECK(target != NULL || parent != NULL,
return 0);
3908 if (target == NULL) {
3909 target = find_entity_by_attr_or_just_name(parent, object_name,
3910 object_href, object_href_val);
3913 if (target == NULL) {
3916 #if XML_PARSER_DEBUG
3918 object_href ?
" " :
"",
3919 object_href ? object_href :
"",
3920 object_href ?
"=" :
"",
3921 object_href ? object_href_val :
"");
3925 object_href ?
" " :
"",
3926 object_href ? object_href :
"",
3927 object_href ?
"=" :
"",
3928 object_href ? object_href_val :
"");
3934 if (as_diff == FALSE) {
3940 xmlAttrPtr pIter = NULL;
3942 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
3943 const char *p_name = (
const char *)pIter->name;
3944 const char *p_value = pcmk__xml_attr_value(pIter);
3952 for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
3953 #if XML_PARSER_DEBUG
3955 object_href ?
" " :
"",
3956 object_href ? object_href :
"",
3957 object_href ?
"=" :
"",
3958 object_href ? object_href_val :
"");
3960 add_xml_object(target, NULL, a_child, as_diff);
3963 #if XML_PARSER_DEBUG
3965 object_href ?
" " :
"",
3966 object_href ? object_href :
"",
3967 object_href ?
"=" :
"",
3968 object_href ? object_href_val :
"");
3976 gboolean can_update = TRUE;
3977 xmlNode *child_of_child = NULL;
3980 CRM_CHECK(to_update != NULL,
return FALSE);
3982 if (
safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
3988 }
else if (can_update) {
3989 #if XML_PARSER_DEBUG
3992 add_xml_object(NULL, child, to_update, FALSE);
3995 for (child_of_child = __xml_first_child(child); child_of_child != NULL;
3996 child_of_child = __xml_next(child_of_child)) {
4009 const char *tag,
const char *field,
const char *value, gboolean search_matches)
4011 int match_found = 0;
4014 CRM_CHECK(children != NULL,
return FALSE);
4016 if (tag != NULL &&
safe_str_neq(tag, crm_element_name(root))) {
4021 if (*children == NULL) {
4028 if (search_matches || match_found == 0) {
4029 xmlNode *child = NULL;
4031 for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
4032 match_found +=
find_xml_children(children, child, tag, field, value, search_matches);
4042 gboolean can_delete = FALSE;
4043 xmlNode *child_of_child = NULL;
4045 const char *up_id = NULL;
4046 const char *child_id = NULL;
4047 const char *right_val = NULL;
4050 CRM_CHECK(update != NULL,
return FALSE);
4053 child_id =
ID(child);
4055 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
4058 if (
safe_str_neq(crm_element_name(update), crm_element_name(child))) {
4061 if (can_delete && delete_only) {
4062 xmlAttrPtr pIter = NULL;
4064 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
4065 const char *p_name = (
const char *)pIter->name;
4066 const char *p_value = pcmk__xml_attr_value(pIter);
4075 if (can_delete && parent != NULL) {
4077 if (delete_only || update == NULL) {
4082 xmlDoc *doc = tmp->doc;
4083 xmlNode *old = NULL;
4086 old = xmlReplaceNode(child, tmp);
4094 xmlDocSetRootElement(doc, old);
4100 }
else if (can_delete) {
4105 child_of_child = __xml_first_child(child);
4106 while (child_of_child) {
4107 xmlNode *next = __xml_next(child_of_child);
4113 child_of_child = NULL;
4115 child_of_child = next;
4125 xmlNode *child = NULL;
4126 GSList *nvpairs = NULL;
4127 xmlNode *result = NULL;
4128 const char *name = NULL;
4132 name = crm_element_name(input);
4141 for (child = __xml_first_child(input); child != NULL;
4142 child = __xml_next(child)) {
4157 xmlNode *match = NULL;
4159 for (match = __xml_first_child(parent); match != NULL; match = __xml_next(match)) {
4165 if (name == NULL || strcmp((
const char *)match->name, name) == 0) {
4182 xmlNode *match = __xml_next(sibling);
4183 const char *name = crm_element_name(sibling);
4185 while (match != NULL) {
4186 if (!strcmp(crm_element_name(match), name)) {
4189 match = __xml_next(match);
4197 static bool init = TRUE;
4206 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
4209 xmlDeregisterNodeDefault(pcmkDeregisterNode);
4210 xmlRegisterNodeDefault(pcmkRegisterNode);
4219 crm_info(
"Cleaning up memory from libxml2");
4224 #define XPATH_MAX 512
4229 const char *tag = NULL;
4230 const char *ref = NULL;
4231 xmlNode *result = input;
4233 if (result == NULL) {
4236 }
else if (top == NULL) {
4240 tag = crm_element_name(result);
4247 if (result == NULL) {
4248 char *nodePath = (
char *)xmlGetNodePath(top);
4250 crm_err(
"No match for %s found in %s: Invalid configuration", xpath_string,
#define pcmk_err_old_data
#define CRM_CHECK(expr, failure_action)
const char * crm_get_tmpdir(void)
xmlNode * find_xml_node(xmlNode *cib, const char *node_path, gboolean must_find)
#define XML_ATTR_UPDATE_ORIG
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
void crm_schema_init(void)
xmlNode * get_message_xml(xmlNode *msg, const char *field)
#define crm_notice(fmt, args...)
#define XML_ATTR_UPDATE_CLIENT
const char * bz2_strerror(int rc)
gboolean safe_str_neq(const char *a, const char *b)
char * crm_generate_uuid(void)
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
#define XML_ATTR_NUMUPDATES
void pcmk__free_acls(GList *acls)
void crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, gboolean formatted)
G_GNUC_INTERNAL int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
xmlNode * first_named_child(const xmlNode *parent, const char *name)
void crm_xml_cleanup(void)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
#define XML_ATTR_UPDATE_USER
int char2score(const char *score)
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
#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.
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
#define XML_NVPAIR_ATTR_NAME
int get_attr_name(const char *input, size_t offset, size_t max)
#define CRM_LOG_ASSERT(expr)
#define do_crm_log_alias(level, file, function, line, fmt, args...)
Log a message as if it came from a different code location.
#define clear_bit(word, bit)
void copy_in_properties(xmlNode *target, xmlNode *src)
void xml_accept_changes(xmlNode *xml)
xmlNode * filename2xml(const char *filename)
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.
void purge_diff_markers(xmlNode *a_node)
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
void pcmk__post_process_acl(xmlNode *xml)
#define CRM_XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
xmlNode * string2xml(const char *input)
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
#define XML_ATTR_GENERATION
xmlDoc * getDocPtr(xmlNode *node)
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
bool xml_tracking_changes(xmlNode *xml)
xmlNode * copy_xml(xmlNode *src_node)
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
#define crm_warn(fmt, args...)
#define pcmk_err_diff_failed
#define set_bit(word, bit)
xmlNode * stdin2xml(void)
#define crm_debug(fmt, args...)
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
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.
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
#define crm_trace(fmt, args...)
#define crm_log_xml_explicit(xml, text)
#define XML_PRIVATE_MAGIC
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
#define crm_log_xml_debug(xml, text)
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
Wrappers for and extensions to libxml2.
bool xml_document_dirty(xmlNode *xml)
xmlNode * create_xml_node(xmlNode *parent, const char *name)
#define crm_log_xml_warn(xml, text)
char * dump_xml_formatted(xmlNode *msg)
G_GNUC_INTERNAL void pcmk__set_xml_flag(xmlNode *xml, enum xml_private_flags flag)
#define XML_DIFF_POSITION
#define XML_TAG_RESOURCE_REF
void xml_acl_disable(xmlNode *xml)
void void crm_destroy_xml(gpointer data)
xmlNode destructor which can be used in glib collections
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
void free_xml(xmlNode *child)
gboolean xml_has_children(const xmlNode *root)
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
gboolean crm_ends_with_ext(const char *s, const char *match)
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
const xmlChar * pcmkXmlStr
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
void xml_log_changes(uint8_t level, const char *function, xmlNode *xml)
#define pcmk_err_diff_resync
#define crm_log_xml_err(xml, text)
#define crm_perror(level, fmt, args...)
Log a system error message.
#define crm_err(fmt, args...)
#define XML_CIB_ATTR_WRITTEN
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
#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)
void xml_remove_prop(xmlNode *obj, const char *name)
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
gboolean can_prune_leaf(xmlNode *xml_node)
int compare_version(const char *version1, const char *version2)
#define crm_log_xml_info(xml, text)
char * xml_get_path(xmlNode *xml)
char * dump_xml_unformatted(xmlNode *msg)
#define XML_ATTR_GENERATION_ADMIN
#define XML_NVPAIR_ATTR_VALUE
#define XML_ATTR_CRM_VERSION
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
void strip_text_nodes(xmlNode *xml)
xmlNode * diff_xml_object(xmlNode *left, xmlNode *right, gboolean suppress)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
int get_tag_name(const char *input, size_t offset, size_t max)
#define XML_CIB_TAG_OBJ_REF
#define crm_log_xml_trace(xml, text)
#define XML_ACL_TAG_ROLE_REF
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, gboolean *changed, const char *marker)
#define XML_CIB_TAG_CONFIGURATION
void xml_log_patchset(uint8_t level, const char *function, xmlNode *xml)
#define safe_str_eq(a, b)
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
char * crm_xml_escape(const char *text)
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
void fix_plus_plus_recursive(xmlNode *target)
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version)
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
#define crm_info(fmt, args...)
const char * crm_xml_add_last_written(xmlNode *xml_node)
char * dump_xml_formatted_with_text(xmlNode *msg)
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
void crm_xml_dump(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
struct xml_deleted_obj_s xml_deleted_obj_t