13 #include <sys/types.h> 21 #include <libxml/parser.h> 22 #include <libxml/tree.h> 23 #include <libxml/xmlIO.h> 32 #ifndef XML_PARSER_DEBUG 33 #define XML_PARSER_DEBUG 0 46 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER) 48 #define CHUNK_SIZE 1024 53 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
65 #define buffer_print(buffer, max, offset, fmt, args...) do { \ 68 rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \ 70 if(buffer && rc < 0) { \ 71 crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \ 72 (buffer)[(offset)] = 0; \ 74 } else if(rc >= ((max) - (offset))) { \ 76 (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \ 77 tmp = pcmk__realloc((buffer), (max)); \ 87 insert_prefix(
int options,
char **buffer,
int *offset,
int *max,
int depth)
90 size_t spaces = 2 * depth;
92 if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
94 (*buffer) = pcmk__realloc((*buffer), (*max));
96 memset((*buffer) + (*offset),
' ', spaces);
102 set_parent_flag(xmlNode *xml,
long flag)
105 for(; xml; xml = xml->parent) {
120 if(xml && xml->doc && xml->doc->_private){
130 mark_xml_node_dirty(xmlNode *xml)
138 reset_xml_node_flags(xmlNode *xml)
140 xmlNode *cIter = NULL;
147 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
148 cIter = pcmk__xml_next(cIter)) {
149 reset_xml_node_flags(cIter);
157 xmlNode *cIter = NULL;
163 mark_xml_node_dirty(xml);
165 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
166 cIter = pcmk__xml_next(cIter)) {
175 xmlNode *parent = a->parent;
181 mark_xml_node_dirty(parent);
184 #define XML_PRIVATE_MAGIC (long) 0x81726354 188 free_deleted_object(
void *
data)
193 free(deleted_obj->
path);
222 free_private_data(xmlNode *node)
231 if (node->type != XML_DOCUMENT_NODE || node->name == NULL
232 || node->name[0] !=
' ') {
233 reset_xml_private_data(node->_private);
234 free(node->_private);
240 new_private_data(xmlNode *node)
245 case XML_ELEMENT_NODE:
246 case XML_DOCUMENT_NODE:
247 case XML_ATTRIBUTE_NODE:
248 case XML_COMMENT_NODE:
257 case XML_CDATA_SECTION_NODE:
261 crm_trace(
"Ignoring %p %d", node, node->type);
270 mark_xml_node_dirty(node);
278 crm_trace(
"Tracking changes%s to %p", enforce_acls?
" with ACLs":
"", xml);
281 if(acl_source == NULL) {
292 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
299 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
317 xmlNode *cIter = NULL;
319 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
332 marked_as_deleted(xmlAttrPtr a,
void *user_data)
345 accept_attr_deletions(xmlNode *xml)
354 for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
355 cIter = pcmk__xml_next(cIter)) {
356 accept_attr_deletions(cIter);
373 if (needle->type == XML_COMMENT_NODE) {
377 const char *
id =
ID(needle);
378 const char *attr = (
id == NULL)? NULL :
XML_ATTR_ID;
380 return pcmk__xe_match(haystack, crm_element_name(needle), attr,
id);
397 doc = xml->doc->_private;
402 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
429 crm_trace(
"Accepting changes to %p", xml);
430 doc = xml->doc->_private;
431 top = xmlDocGetRootElement(xml->doc);
433 reset_xml_private_data(xml->doc->_private);
441 accept_attr_deletions(top);
447 xmlNode *a_child = NULL;
448 const char *
name =
"NULL";
451 name = crm_element_name(root);
454 if (search_path == NULL) {
459 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
460 a_child = pcmk__xml_next(a_child)) {
461 if (strcmp((
const char *)a_child->name, search_path) == 0) {
468 crm_warn(
"Could not find %s in %s.", search_path,
name);
469 }
else if (root != NULL) {
472 crm_trace(
"Could not find %s in <NULL>.", search_path);
478 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \ 496 const char *attr_n,
const char *attr_v)
499 CRM_CHECK(attr_n == NULL || attr_v != NULL,
return NULL);
501 for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
502 child = pcmk__xml_next(child)) {
503 if (pcmk__str_eq(node_name, (
const char *) (child->name),
505 && ((attr_n == NULL) ||
attr_matches(child, attr_n, attr_v))) {
509 crm_trace(
"XML child node <%s%s%s%s%s> not found in %s",
510 (node_name? node_name :
"(any)"),
512 (attr_n? attr_n :
""),
514 (attr_n? attr_v :
""),
515 crm_element_name(parent));
523 crm_warn(
"No node to copy properties from");
525 }
else if (
target == NULL) {
526 crm_err(
"No node to copy properties into");
529 for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
530 const char *p_name = (
const char *) a->name;
531 const char *p_value = pcmk__xml_attr_value(a);
544 xmlNode *child = NULL;
546 for (xmlAttrPtr a = pcmk__xe_first_attr(
target); a != NULL; a = a->next) {
547 const char *p_name = (
const char *) a->name;
548 const char *p_value = pcmk__xml_attr_value(a);
552 for (child = pcmk__xml_first_child(
target); child != NULL;
553 child = pcmk__xml_next(child)) {
566 const char *old_value = NULL;
568 if (value == NULL ||
name == NULL) {
574 if (old_value == NULL) {
578 }
else if (strstr(value,
name) != value) {
582 name_len = strlen(
name);
583 value_len = strlen(value);
584 if (value_len < (name_len + 2)
585 || value[name_len] !=
'+' || (value[name_len + 1] !=
'+' && value[name_len + 1] !=
'=')) {
592 if (old_value != value) {
596 if (value[name_len + 1] !=
'+') {
597 const char *offset_s = value + (name_len + 2);
611 if (old_value == value) {
630 bool (*match)(xmlAttrPtr,
void *),
633 xmlAttrPtr next = NULL;
635 for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
637 if ((match == NULL) || match(a, user_data)) {
639 crm_trace(
"ACLs prevent removal of attributes (%s and " 640 "possibly others) from %s element",
641 (
const char *) a->name, (
const char *) element->name);
667 xmlDocSetRootElement(doc, node);
668 xmlSetTreeDoc(node, doc);
676 xmlNode *child = NULL;
679 CRM_CHECK(src_node != NULL,
return NULL);
681 child = xmlDocCopyNode(src_node, doc, 1);
682 xmlAddChild(parent, child);
699 xmlNode *node = NULL;
701 if (pcmk__str_empty(
name)) {
706 if (parent == NULL) {
709 xmlDocSetRootElement(doc, node);
714 xmlAddChild(parent, node);
726 xmlNodeSetContent(node, (
pcmkXmlStr) content);
734 const char *class_name,
const char *text)
738 if (class_name != NULL) {
762 free_xml_with_position(xmlNode * child,
int position)
766 xmlDoc *doc = child->doc;
770 top = xmlDocGetRootElement(doc);
773 if (doc != NULL && top == child) {
792 sizeof(buffer)) > 0) {
795 crm_trace(
"Deleting %s %p from %p", buffer, child, doc);
798 deleted_obj->
path = strdup(buffer);
802 if (child->type == XML_COMMENT_NODE) {
826 free_xml_with_position(child, -1);
833 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
835 xmlDocSetRootElement(doc, copy);
836 xmlSetTreeDoc(copy, doc);
841 log_xmllib_err(
void *ctx,
const char *fmt, ...)
846 log_xmllib_err(
void *ctx, const
char *fmt, ...)
849 static struct qb_log_callsite *xml_error_cs = NULL;
851 if (xml_error_cs == NULL) {
852 xml_error_cs = qb_log_callsite_get(
857 if (xml_error_cs && xml_error_cs->targets) {
859 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__,
"xml library error",
861 "XML Error: ", fmt, ap);
872 xmlDocPtr output = NULL;
873 xmlParserCtxtPtr ctxt = NULL;
874 xmlErrorPtr last_error = NULL;
877 crm_err(
"Can't parse NULL input");
882 ctxt = xmlNewParserCtxt();
885 xmlCtxtResetLastError(ctxt);
886 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
887 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
890 xml = xmlDocGetRootElement(output);
892 last_error = xmlCtxtGetLastError(ctxt);
893 if (last_error && last_error->code != XML_ERR_OK) {
899 crm_warn(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
900 last_error->domain, last_error->level, last_error->code, last_error->message);
902 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
905 }
else if (last_error->code != XML_ERR_DOCUMENT_END) {
906 crm_err(
"Couldn't%s parse %d chars: %s", xml ?
" fully" :
"", (
int)strlen(input),
913 int len = strlen(input);
917 crm_warn(
"Parse error[+%.3d]: %.80s", lpc, input+lpc);
925 xmlFreeParserCtxt(ctxt);
932 size_t data_length = 0;
933 size_t read_chars = 0;
935 char *xml_buffer = NULL;
936 xmlNode *xml_obj = NULL;
942 data_length += read_chars;
945 if (data_length == 0) {
946 crm_warn(
"No XML supplied on stdin");
951 xml_buffer[data_length] =
'\0';
960 decompress_file(
const char *filename)
964 size_t length = 0, read_len = 0;
965 BZFILE *bz_file = NULL;
966 FILE *input = fopen(filename,
"r");
969 crm_perror(LOG_ERR,
"Could not open %s for reading", filename);
973 bz_file = BZ2_bzReadOpen(&
rc, input, 0, 0, NULL, 0);
975 crm_err(
"Could not prepare to read compressed %s: %s " 977 BZ2_bzReadClose(&
rc, bz_file);
984 while (
rc == BZ_OK) {
988 crm_trace(
"Read %ld bytes from file: %d", (
long)read_len,
rc);
990 if (
rc == BZ_OK ||
rc == BZ_STREAM_END) {
995 buffer[length] =
'\0';
997 if (
rc != BZ_STREAM_END) {
998 crm_err(
"Could not read compressed %s: %s " 1004 BZ2_bzReadClose(&
rc, bz_file);
1018 xmlNode *iter = xml->children;
1021 xmlNode *next = iter->next;
1023 switch (iter->type) {
1029 case XML_ELEMENT_NODE:
1046 xmlNode *xml = NULL;
1047 xmlDocPtr output = NULL;
1048 bool uncompressed =
true;
1049 xmlParserCtxtPtr ctxt = NULL;
1050 xmlErrorPtr last_error = NULL;
1053 ctxt = xmlNewParserCtxt();
1056 xmlCtxtResetLastError(ctxt);
1057 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1065 output = xmlCtxtReadFd(ctxt, STDIN_FILENO,
"unknown.xml", NULL,
1068 }
else if (uncompressed) {
1072 char *input = decompress_file(filename);
1074 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
1079 if (output && (xml = xmlDocGetRootElement(output))) {
1083 last_error = xmlCtxtGetLastError(ctxt);
1084 if (last_error && last_error->code != XML_ERR_OK) {
1090 crm_err(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
1091 last_error->domain, last_error->level, last_error->code, last_error->message);
1093 if (last_error && last_error->code != XML_ERR_OK) {
1094 crm_err(
"Couldn't%s parse %s", xml ?
" fully" :
"", filename);
1101 xmlFreeParserCtxt(ctxt);
1119 now_str ? now_str :
"Could not determine current time");
1132 for (c =
id; *c; ++c) {
1157 va_start(ap, format);
1158 len = vasprintf(&
id, format, ap);
1180 write_xml_stream(xmlNode *xml_node,
const char *filename, FILE *stream,
1181 bool compress,
unsigned int *nbytes)
1184 char *buffer = NULL;
1196 unsigned int in = 0;
1197 BZFILE *bz_file = NULL;
1200 bz_file = BZ2_bzWriteOpen(&
rc, stream, 5, 0, 30);
1202 crm_warn(
"Not compressing %s: could not prepare file stream: %s " 1205 BZ2_bzWrite(&
rc, bz_file, buffer, strlen(buffer));
1207 crm_warn(
"Not compressing %s: could not compress data: %s " 1208 CRM_XS " bzerror=%d errno=%d",
1214 BZ2_bzWriteClose(&
rc, bz_file, 0, &in, nbytes);
1216 crm_warn(
"Not compressing %s: could not write compressed data: %s " 1217 CRM_XS " bzerror=%d errno=%d",
1221 crm_trace(
"Compressed XML for %s from %u bytes to %u",
1222 filename, in, *nbytes);
1229 rc = fprintf(stream,
"%s", buffer);
1234 *nbytes = (
unsigned int)
rc;
1241 if (fflush(stream) != 0) {
1243 crm_perror(LOG_ERR,
"flushing %s", filename);
1247 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1249 crm_perror(LOG_ERR,
"synchronizing %s", filename);
1254 crm_trace(
"Saved %d bytes to %s as XML", *nbytes, filename);
1271 write_xml_fd(xmlNode * xml_node,
const char *filename,
int fd, gboolean compress)
1273 FILE *stream = NULL;
1274 unsigned int nbytes = 0;
1277 CRM_CHECK(xml_node && (fd > 0),
return -EINVAL);
1278 stream = fdopen(fd,
"w");
1279 if (stream == NULL) {
1282 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1286 return (
int) nbytes;
1301 FILE *stream = NULL;
1302 unsigned int nbytes = 0;
1305 CRM_CHECK(xml_node && filename,
return -EINVAL);
1306 stream = fopen(filename,
"w");
1307 if (stream == NULL) {
1310 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1314 return (
int) nbytes;
1319 replace_text(
char *text,
int start,
size_t *length,
const char *replace)
1321 size_t offset = strlen(replace) - 1;
1324 text = pcmk__realloc(text, *length);
1326 for (
size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1327 text[lpc] = text[lpc - offset];
1330 memcpy(text + start, replace, offset + 1);
1368 length = 1 + strlen(text);
1369 copy = strdup(text);
1371 for (
size_t index = 0; index < length; index++) {
1372 switch (copy[index]) {
1376 copy = replace_text(copy, index, &length,
"<");
1379 copy = replace_text(copy, index, &length,
">");
1382 copy = replace_text(copy, index, &length,
""");
1385 copy = replace_text(copy, index, &length,
"'");
1388 copy = replace_text(copy, index, &length,
"&");
1392 copy = replace_text(copy, index, &length,
" ");
1395 copy = replace_text(copy, index, &length,
"\\n");
1398 copy = replace_text(copy, index, &length,
"\\r");
1402 if(copy[index] <
' ' || copy[index] >
'~') {
1405 copy = replace_text(copy, index, &length, replace);
1414 dump_xml_attr(xmlAttrPtr attr,
int options,
char **buffer,
int *offset,
int *max)
1416 char *p_value = NULL;
1417 const char *p_name = NULL;
1421 if (attr == NULL || attr->children == NULL) {
1430 p_name = (
const char *)attr->name;
1439 pcmk__xe_log(
int log_level,
const char *file,
const char *
function,
int line,
1440 const char *prefix, xmlNode *
data,
int depth,
int options)
1444 const char *
name = NULL;
1445 const char *hidden = NULL;
1447 xmlNode *child = NULL;
1456 char *buffer = NULL;
1458 insert_prefix(options, &buffer, &offset, &max, depth);
1460 if (
data->type == XML_COMMENT_NODE) {
1467 for (xmlAttrPtr a = pcmk__xe_first_attr(
data); a != NULL;
1471 const char *p_name = (
const char *) a->name;
1472 const char *p_value = pcmk__xml_attr_value(a);
1473 char *p_copy = NULL;
1477 }
else if (pcmk_any_flags_set(options,
1483 }
else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1484 p_copy = strdup(
"*****");
1506 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
1510 if(
data->type == XML_COMMENT_NODE) {
1520 for (child = pcmk__xml_first_child(
data); child != NULL;
1521 child = pcmk__xml_next(child)) {
1522 pcmk__xe_log(log_level, file,
function, line, prefix, child,
1529 char *buffer = NULL;
1531 insert_prefix(options, &buffer, &offset, &max, depth);
1534 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
1541 log_xml_changes(
int log_level,
const char *file,
const char *
function,
int line,
1542 const char *prefix, xmlNode *
data,
int depth,
int options)
1545 char *prefix_m = NULL;
1546 xmlNode *child = NULL;
1554 prefix_m = strdup(prefix);
1564 char *spaces = calloc(80, 1);
1565 int s_count = 0, s_max = 80;
1566 char *prefix_del = NULL;
1567 char *prefix_moved = NULL;
1568 const char *
flags = prefix;
1570 insert_prefix(options, &spaces, &s_count, &s_max, depth);
1571 prefix_del = strdup(prefix);
1572 prefix_del[0] =
'-';
1573 prefix_del[1] =
'-';
1574 prefix_moved = strdup(prefix);
1575 prefix_moved[1] =
'~';
1578 flags = prefix_moved;
1586 for (xmlAttrPtr a = pcmk__xe_first_attr(
data); a != NULL; a = a->next) {
1587 const char *aname = (
const char*) a->name;
1594 "%s %s @%s=%s",
flags, spaces, aname, value);
1606 flags = prefix_moved;
1612 "%s %s @%s=%s",
flags, spaces, aname, value);
1619 for (child = pcmk__xml_first_child(
data); child != NULL;
1620 child = pcmk__xml_next(child)) {
1621 log_xml_changes(log_level, file,
function, line, prefix, child,
1622 depth + 1, options);
1629 for (child = pcmk__xml_first_child(
data); child != NULL;
1630 child = pcmk__xml_next(child)) {
1631 log_xml_changes(log_level, file,
function, line, prefix, child,
1632 depth + 1, options);
1642 const char *prefix, xmlNode *
data,
int depth,
int options)
1644 xmlNode *a_child = NULL;
1646 char *prefix_m = NULL;
1652 if (prefix == NULL) {
1659 "No data to dump as XML");
1664 log_xml_changes(log_level, file,
function, line, prefix,
data, depth,
1673 prefix_m = strdup(prefix);
1680 prefix_m = strdup(prefix);
1689 for (a_child = pcmk__xml_first_child(
data); a_child != NULL;
1690 a_child = pcmk__xml_next(a_child)) {
1691 log_data_element(log_level, file,
function, line, prefix, a_child, depth + 1, options);
1702 dump_filtered_xml(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max)
1704 for (xmlAttrPtr a = pcmk__xe_first_attr(
data); a != NULL; a = a->next) {
1706 dump_xml_attr(a, options, buffer, offset, max);
1712 dump_xml_element(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1714 const char *
name = NULL;
1725 if (*buffer == NULL) {
1733 insert_prefix(options, buffer, offset, max, depth);
1737 dump_filtered_xml(
data, options, buffer, offset, max);
1740 for (xmlAttrPtr a = pcmk__xe_first_attr(
data); a != NULL; a = a->next) {
1741 dump_xml_attr(a, options, buffer, offset, max);
1745 if (
data->children == NULL) {
1756 if (
data->children) {
1757 xmlNode *xChild = NULL;
1758 for(xChild =
data->children; xChild != NULL; xChild = xChild->next) {
1762 insert_prefix(options, buffer, offset, max, depth);
1772 dump_xml_text(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1783 if (*buffer == NULL) {
1788 insert_prefix(options, buffer, offset, max, depth);
1798 dump_xml_cdata(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1809 if (*buffer == NULL) {
1814 insert_prefix(options, buffer, offset, max, depth);
1826 dump_xml_comment(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1837 if (*buffer == NULL) {
1842 insert_prefix(options, buffer, offset, max, depth);
1853 #define PCMK__XMLDUMP_STATS 0 1868 int *max,
int depth)
1885 #if (PCMK__XMLDUMP_STATS - 0) 1886 time_t next,
new = time(NULL);
1889 xmlOutputBuffer *xml_buffer;
1895 xml_buffer = xmlAllocOutputBuffer(NULL);
1908 xmlNodeDumpOutput(xml_buffer, doc,
data, 0,
1911 (void) xmlOutputBufferWrite(xml_buffer,
sizeof(
"\n") - 1,
"\n");
1912 if (xml_buffer->buffer != NULL) {
1914 (
char *) xmlBufContent(xml_buffer->buffer));
1917 #if (PCMK__XMLDUMP_STATS - 0) 1919 if ((now + 1) < next) {
1921 crm_err(
"xmlNodeDump() -> %dbytes took %ds", *max, next - now);
1926 (void) xmlOutputBufferClose(xml_buffer);
1930 switch(
data->type) {
1931 case XML_ELEMENT_NODE:
1933 dump_xml_element(
data, options, buffer, offset, max, depth);
1938 dump_xml_text(
data, options, buffer, offset, max, depth);
1941 case XML_COMMENT_NODE:
1942 dump_xml_comment(
data, options, buffer, offset, max, depth);
1944 case XML_CDATA_SECTION_NODE:
1945 dump_xml_cdata(
data, options, buffer, offset, max, depth);
1992 char *buffer = NULL;
1993 int offset = 0, max = 0;
1997 &buffer, &offset, &max, 0);
2004 char *buffer = NULL;
2005 int offset = 0, max = 0;
2015 char *buffer = NULL;
2016 int offset = 0, max = 0;
2025 if (xml_root != NULL && xml_root->children != NULL) {
2055 if (filename == NULL) {
2063 crm_info(
"Saving %s to %s", desc, filename);
2078 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2093 mark_attr_deleted(xmlNode *new_xml,
const char *element,
const char *attr_name,
2094 const char *old_value)
2097 xmlAttr *attr = NULL;
2113 crm_trace(
"XML attribute %s=%s was removed from %s",
2114 attr_name, old_value, element);
2122 mark_attr_changed(xmlNode *new_xml,
const char *element,
const char *attr_name,
2123 const char *old_value)
2127 crm_trace(
"XML attribute %s was changed from '%s' to '%s' in %s",
2128 attr_name, old_value, vcopy, element);
2143 mark_attr_moved(xmlNode *new_xml,
const char *element, xmlAttr *old_attr,
2144 xmlAttr *new_attr,
int p_old,
int p_new)
2148 crm_trace(
"XML attribute %s moved from position %d to %d in %s",
2149 old_attr->name, p_old, p_new, element);
2152 mark_xml_node_dirty(new_xml);
2157 p = (p_old > p_new)? old_attr->_private : new_attr->_private;
2166 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2168 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2170 while (attr_iter != NULL) {
2171 xmlAttr *old_attr = attr_iter;
2172 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2173 const char *
name = (
const char *) attr_iter->name;
2176 attr_iter = attr_iter->next;
2177 if (new_attr == NULL) {
2178 mark_attr_deleted(new_xml, (
const char *) old_xml->name,
name,
2192 if (strcmp(new_value, old_value) != 0) {
2193 mark_attr_changed(new_xml, (
const char *) old_xml->name,
name,
2196 }
else if ((old_pos != new_pos)
2198 mark_attr_moved(new_xml, (
const char *) old_xml->name,
2199 old_attr, new_attr, old_pos, new_pos);
2210 mark_created_attrs(xmlNode *new_xml)
2212 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2214 while (attr_iter != NULL) {
2215 xmlAttr *new_attr = attr_iter;
2218 attr_iter = attr_iter->next;
2220 const char *attr_name = (
const char *) new_attr->name;
2222 crm_trace(
"Created new attribute %s=%s in %s",
2233 xmlUnsetProp(new_xml, new_attr->name);
2244 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2247 xml_diff_old_attrs(old_xml, new_xml);
2248 mark_created_attrs(new_xml);
2261 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2267 reset_xml_node_flags(candidate);
2273 free_xml_with_position(candidate,
2283 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2284 int p_old,
int p_new)
2288 crm_trace(
"Child element %s with id='%s' moved from position %d to %d under %s",
2289 new_child->name, (
ID(new_child)?
ID(new_child) :
"<no id>"),
2290 p_old, p_new, new_parent->name);
2291 mark_xml_node_dirty(new_parent);
2294 if (p_old > p_new) {
2295 p = old_child->_private;
2297 p = new_child->_private;
2304 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml,
bool check_top)
2306 xmlNode *cIter = NULL;
2310 if (old_xml == NULL) {
2316 p = new_xml->_private;
2325 xml_diff_attrs(old_xml, new_xml);
2328 for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2329 xmlNode *old_child = cIter;
2332 cIter = pcmk__xml_next(cIter);
2334 mark_xml_changes(old_child, new_child, TRUE);
2337 mark_child_deleted(old_child, new_xml);
2342 for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2343 xmlNode *new_child = cIter;
2346 cIter = pcmk__xml_next(cIter);
2347 if(old_child == NULL) {
2349 p = new_child->_private;
2351 mark_xml_changes(old_child, new_child, TRUE);
2358 if(p_old != p_new) {
2359 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2383 mark_xml_changes(old_xml, new_xml, FALSE);
2389 xmlNode *cIter = NULL;
2390 gboolean can_prune = TRUE;
2391 const char *
name = crm_element_name(xml_node);
2398 for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2399 const char *p_name = (
const char *) a->name;
2407 cIter = pcmk__xml_first_child(xml_node);
2409 xmlNode *child = cIter;
2411 cIter = pcmk__xml_next(cIter);
2432 xmlNode *a_child = NULL;
2435 CRM_CHECK(search_comment->type == XML_COMMENT_NODE,
return NULL);
2437 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2438 a_child = pcmk__xml_next(a_child)) {
2443 if (offset < search_offset) {
2446 }
else if (offset > search_offset) {
2455 if (a_child->type == XML_COMMENT_NODE
2456 && pcmk__str_eq((
const char *)a_child->content, (
const char *)search_comment->content,
pcmk__str_casei)) {
2482 CRM_CHECK(update->type == XML_COMMENT_NODE,
return);
2491 }
else if (!pcmk__str_eq((
const char *)
target->content, (
const char *)update->content,
pcmk__str_casei)) {
2492 xmlFree(
target->content);
2493 target->content = xmlStrdup(update->content);
2513 xmlNode *a_child = NULL;
2514 const char *object_name = NULL,
2515 *object_href = NULL,
2516 *object_href_val = NULL;
2518 #if XML_PARSER_DEBUG 2525 if (update->type == XML_COMMENT_NODE) {
2530 object_name = crm_element_name(update);
2531 object_href_val =
ID(update);
2532 if (object_href_val != NULL) {
2544 object_href, object_href_val);
2550 #if XML_PARSER_DEBUG 2552 object_href ?
" " :
"",
2553 object_href ? object_href :
"",
2554 object_href ?
"=" :
"",
2555 object_href ? object_href_val :
"");
2559 object_href ?
" " :
"",
2560 object_href ? object_href :
"",
2561 object_href ?
"=" :
"",
2562 object_href ? object_href_val :
"");
2566 CRM_CHECK(pcmk__str_eq(crm_element_name(
target), crm_element_name(update),
2570 if (as_diff == FALSE) {
2576 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2578 const char *p_value = pcmk__xml_attr_value(a);
2581 xmlUnsetProp(
target, a->name);
2586 for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2587 a_child = pcmk__xml_next(a_child)) {
2588 #if XML_PARSER_DEBUG 2590 object_href ?
" " :
"",
2591 object_href ? object_href :
"",
2592 object_href ?
"=" :
"",
2593 object_href ? object_href_val :
"");
2598 #if XML_PARSER_DEBUG 2600 object_href ?
" " :
"",
2601 object_href ? object_href :
"",
2602 object_href ?
"=" :
"",
2603 object_href ? object_href_val :
"");
2610 gboolean can_update = TRUE;
2611 xmlNode *child_of_child = NULL;
2614 CRM_CHECK(to_update != NULL,
return FALSE);
2616 if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child),
pcmk__str_none)) {
2622 }
else if (can_update) {
2623 #if XML_PARSER_DEBUG 2629 for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2630 child_of_child = pcmk__xml_next(child_of_child)) {
2643 const char *tag,
const char *field,
const char *value, gboolean search_matches)
2645 int match_found = 0;
2648 CRM_CHECK(children != NULL,
return FALSE);
2650 if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root),
pcmk__str_casei)) {
2655 if (*children == NULL) {
2662 if (search_matches || match_found == 0) {
2663 xmlNode *child = NULL;
2665 for (child = pcmk__xml_first_child(root); child != NULL;
2666 child = pcmk__xml_next(child)) {
2667 match_found +=
find_xml_children(children, child, tag, field, value, search_matches);
2677 gboolean can_delete = FALSE;
2678 xmlNode *child_of_child = NULL;
2680 const char *up_id = NULL;
2681 const char *child_id = NULL;
2682 const char *right_val = NULL;
2685 CRM_CHECK(update != NULL,
return FALSE);
2688 child_id =
ID(child);
2690 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2693 if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child),
pcmk__str_casei)) {
2696 if (can_delete && delete_only) {
2697 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2699 const char *p_name = (
const char *) a->name;
2700 const char *p_value = pcmk__xml_attr_value(a);
2709 if (can_delete && parent != NULL) {
2711 if (delete_only || update == NULL) {
2716 xmlDoc *doc = tmp->doc;
2717 xmlNode *old = NULL;
2720 old = xmlReplaceNode(child, tmp);
2728 xmlDocSetRootElement(doc, old);
2734 }
else if (can_delete) {
2739 child_of_child = pcmk__xml_first_child(child);
2740 while (child_of_child) {
2741 xmlNode *next = pcmk__xml_next(child_of_child);
2747 child_of_child = NULL;
2749 child_of_child = next;
2759 xmlNode *child = NULL;
2760 GSList *nvpairs = NULL;
2761 xmlNode *result = NULL;
2762 const char *
name = NULL;
2766 name = crm_element_name(input);
2775 for (child = pcmk__xml_first_child(input); child != NULL;
2776 child = pcmk__xml_next(child)) {
2791 xmlNode *match = NULL;
2793 for (match = pcmk__xe_first_child(parent); match != NULL;
2794 match = pcmk__xe_next(match)) {
2817 xmlNode *match = pcmk__xe_next(sibling);
2818 const char *
name = crm_element_name(sibling);
2820 while (match != NULL) {
2821 if (!strcmp(crm_element_name(match),
name)) {
2824 match = pcmk__xe_next(match);
2832 static bool init =
true;
2841 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2844 xmlDeregisterNodeDefault(free_private_data);
2845 xmlRegisterNodeDefault(new_private_data);
2854 crm_info(
"Cleaning up memory from libxml2");
2859 #define XPATH_MAX 512 2864 const char *tag = NULL;
2865 const char *ref = NULL;
2866 xmlNode *result = input;
2868 if (result == NULL) {
2871 }
else if (top == NULL) {
2875 tag = crm_element_name(result);
2882 if (result == NULL) {
2883 char *nodePath = (
char *)xmlGetNodePath(top);
2885 crm_err(
"No match for %s found in %s: Invalid configuration", xpath_string,
2903 static const char *base = NULL;
2907 base = getenv(
"PCMK_schema_directory");
2909 if (pcmk__str_empty(base)) {
2923 crm_err(
"XML artefact family specified as %u not recognized", ns);
2943 crm_err(
"XML artefact family specified as %u not recognized", ns);
2954 const char *
name, *value;
2956 name = va_arg(pairs,
const char *);
2961 value = va_arg(pairs,
const char *);
2962 if (value != NULL) {
2972 va_start(pairs, node);
#define CRM_CHECK(expr, failure_action)
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
void crm_schema_init(void)
#define PCMK__XML_PARSE_OPTS
void pcmk__xml2text(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
const char * bz2_strerror(int rc)
char * crm_generate_uuid(void)
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)
void pcmk__free_acls(GList *acls)
int pcmk_rc2legacy(int rc)
xmlNode * pcmk__xml_match(xmlNode *haystack, xmlNode *needle, bool exact)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
G_GNUC_INTERNAL int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
int char2score(const char *score)
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
void fix_plus_plus_recursive(xmlNode *target)
void crm_xml_init(void)
Initialize the CRM XML subsystem.
xmlNode * first_named_child(const xmlNode *parent, const char *name)
#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__xe_log(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, int options)
void pcmk_free_xml_subtree(xmlNode *xml)
void pcmk__xe_set_props(xmlNodePtr node,...)
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
xmlNode * stdin2xml(void)
#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.
unsigned int crm_trace_nonlog
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node's attributes.
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Deprecated Pacemaker XML API.
xmlNode * filename2xml(const char *filename)
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)
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
#define crm_warn(fmt, args...)
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
void pcmk__strip_xml_text(xmlNode *xml)
xmlNode * copy_xml(xmlNode *src)
int pcmk__xml_position(xmlNode *xml, enum xml_private_flags ignore_if_set)
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)
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
void free_xml(xmlNode *child)
#define crm_trace(fmt, args...)
#define XML_PRIVATE_MAGIC
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
#define crm_log_xml_debug(xml, text)
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Wrappers for and extensions to libxml2.
#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.
void pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
#define XML_TAG_RESOURCE_REF
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
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)
xmlNode * string2xml(const char *input)
void xml_log_changes(uint8_t log_level, const char *function, xmlNode *xml)
const char * pcmk__epoch2str(time_t *when)
const xmlChar * pcmkXmlStr
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
char * dump_xml_unformatted(xmlNode *an_xml_node)
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
#define PCMK__BUFFER_SIZE
void copy_in_properties(xmlNode *target, xmlNode *src)
#define crm_log_xml_err(xml, text)
xmlNode * pcmk__xe_match(xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
gboolean xml_has_children(const xmlNode *xml_root)
#define crm_err(fmt, args...)
#define CRM_SCHEMA_DIRECTORY
#define XML_CIB_ATTR_WRITTEN
const char * pcmk__get_tmpdir(void)
#define XML_ACL_TAG_ROLE_REFv1
void pcmk__mark_xml_created(xmlNode *xml)
void pcmk__apply_acl(xmlNode *xml)
xmlNode * pcmk__xc_match(xmlNode *root, xmlNode *search_comment, bool exact)
xmlNode * find_xml_node(xmlNode *root, const char *search_path, gboolean must_find)
void xml_accept_changes(xmlNode *xml)
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)
Replace special characters with their XML escape sequences.
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
#define XML_CIB_TAG_OBJ_REF
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
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
#define attr_matches(c, n, v)
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
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 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
void xml_remove_prop(xmlNode *obj, const char *name)
const char * pcmk__xe_add_last_written(xmlNode *xe)
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
#define crm_info(fmt, args...)
bool pcmk__ends_with_ext(const char *s, const char *match)
gboolean can_prune_leaf(xmlNode *xml_node)
bool xml_document_dirty(xmlNode *xml)