13 #include <sys/types.h> 21 #include <libxml/parser.h> 22 #include <libxml/tree.h> 23 #include <libxml/xmlIO.h> 33 #ifndef XML_PARSER_DEBUG 34 #define XML_PARSER_DEBUG 0 47 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER) 49 #define CHUNK_SIZE 1024 54 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
66 #define buffer_print(buffer, max, offset, fmt, args...) do { \ 69 rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \ 71 if(buffer && rc < 0) { \ 72 crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \ 73 (buffer)[(offset)] = 0; \ 75 } else if(rc >= ((max) - (offset))) { \ 77 (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \ 78 tmp = pcmk__realloc((buffer), (max)); \ 88 insert_prefix(
int options,
char **buffer,
int *offset,
int *max,
int depth)
91 size_t spaces = 2 * depth;
93 if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
95 (*buffer) = pcmk__realloc((*buffer), (*max));
97 memset((*buffer) + (*offset),
' ', spaces);
103 set_parent_flag(xmlNode *xml,
long flag)
106 for(; xml; xml = xml->parent) {
121 if(xml && xml->doc && xml->doc->_private){
131 mark_xml_node_dirty(xmlNode *xml)
139 reset_xml_node_flags(xmlNode *xml)
141 xmlNode *cIter = NULL;
148 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
149 cIter = pcmk__xml_next(cIter)) {
150 reset_xml_node_flags(cIter);
158 xmlNode *cIter = NULL;
164 mark_xml_node_dirty(xml);
166 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
167 cIter = pcmk__xml_next(cIter)) {
176 xmlNode *parent = a->parent;
182 mark_xml_node_dirty(parent);
185 #define XML_PRIVATE_MAGIC (long) 0x81726354 189 free_deleted_object(
void *
data)
194 free(deleted_obj->
path);
223 free_private_data(xmlNode *node)
232 if (node->type != XML_DOCUMENT_NODE || node->name == NULL
233 || node->name[0] !=
' ') {
234 reset_xml_private_data(node->_private);
235 free(node->_private);
241 new_private_data(xmlNode *node)
246 case XML_ELEMENT_NODE:
247 case XML_DOCUMENT_NODE:
248 case XML_ATTRIBUTE_NODE:
249 case XML_COMMENT_NODE:
258 case XML_CDATA_SECTION_NODE:
262 crm_trace(
"Ignoring %p %d", node, node->type);
271 mark_xml_node_dirty(node);
279 crm_trace(
"Tracking changes%s to %p", enforce_acls?
" with ACLs":
"", xml);
282 if(acl_source == NULL) {
293 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
300 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
318 xmlNode *cIter = NULL;
320 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
333 accept_attr_deletions(xmlNode *xml)
335 xmlNode *cIter = NULL;
336 xmlAttr *pIter = NULL;
340 pIter = pcmk__first_xml_attr(xml);
342 while (pIter != NULL) {
343 const xmlChar *
name = pIter->name;
356 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
357 cIter = pcmk__xml_next(cIter)) {
358 accept_attr_deletions(cIter);
375 if (needle->type == XML_COMMENT_NODE) {
379 const char *
id =
ID(needle);
380 const char *attr = (
id == NULL)? NULL :
XML_ATTR_ID;
382 return pcmk__xe_match(haystack, crm_element_name(needle), attr,
id);
399 doc = xml->doc->_private;
404 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
431 crm_trace(
"Accepting changes to %p", xml);
432 doc = xml->doc->_private;
433 top = xmlDocGetRootElement(xml->doc);
435 reset_xml_private_data(xml->doc->_private);
443 accept_attr_deletions(top);
449 xmlNode *a_child = NULL;
450 const char *
name =
"NULL";
453 name = crm_element_name(root);
456 if (search_path == NULL) {
461 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
462 a_child = pcmk__xml_next(a_child)) {
463 if (strcmp((
const char *)a_child->name, search_path) == 0) {
470 crm_warn(
"Could not find %s in %s.", search_path,
name);
471 }
else if (root != NULL) {
474 crm_trace(
"Could not find %s in <NULL>.", search_path);
480 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \ 498 const char *attr_n,
const char *attr_v)
501 CRM_CHECK(attr_n == NULL || attr_v != NULL,
return NULL);
503 for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
504 child = pcmk__xml_next(child)) {
505 if (pcmk__str_eq(node_name, (
const char *) (child->name),
507 && ((attr_n == NULL) ||
attr_matches(child, attr_n, attr_v))) {
511 crm_trace(
"XML child node <%s%s%s%s%s> not found in %s",
512 (node_name? node_name :
"(any)"),
514 (attr_n? attr_n :
""),
516 (attr_n? attr_v :
""),
517 crm_element_name(parent));
525 crm_warn(
"No node to copy properties from");
527 }
else if (
target == NULL) {
528 crm_err(
"No node to copy properties into");
531 xmlAttrPtr pIter = NULL;
533 for (pIter = pcmk__first_xml_attr(src); pIter != NULL; pIter = pIter->next) {
534 const char *p_name = (
const char *)pIter->name;
535 const char *p_value = pcmk__xml_attr_value(pIter);
548 xmlNode *child = NULL;
549 xmlAttrPtr pIter = NULL;
551 for (pIter = pcmk__first_xml_attr(
target); pIter != NULL; pIter = pIter->next) {
552 const char *p_name = (
const char *)pIter->name;
553 const char *p_value = pcmk__xml_attr_value(pIter);
557 for (child = pcmk__xml_first_child(
target); child != NULL;
558 child = pcmk__xml_next(child)) {
571 const char *old_value = NULL;
573 if (value == NULL ||
name == NULL) {
579 if (old_value == NULL) {
583 }
else if (strstr(value,
name) != value) {
587 name_len = strlen(
name);
588 value_len = strlen(value);
589 if (value_len < (name_len + 2)
590 || value[name_len] !=
'+' || (value[name_len + 1] !=
'+' && value[name_len + 1] !=
'=')) {
597 if (old_value != value) {
601 if (value[name_len + 1] !=
'+') {
602 const char *offset_s = value + (name_len + 2);
616 if (old_value == value) {
634 xmlDocSetRootElement(doc, node);
635 xmlSetTreeDoc(node, doc);
643 xmlNode *child = NULL;
646 CRM_CHECK(src_node != NULL,
return NULL);
648 child = xmlDocCopyNode(src_node, doc, 1);
649 xmlAddChild(parent, child);
666 xmlNode *node = NULL;
668 if (pcmk__str_empty(
name)) {
673 if (parent == NULL) {
676 xmlDocSetRootElement(doc, node);
681 xmlAddChild(parent, node);
693 xmlNodeSetContent(node, (
pcmkXmlStr) content);
701 const char *class_name,
const char *text)
705 if (class_name != NULL) {
729 free_xml_with_position(xmlNode * child,
int position)
733 xmlDoc *doc = child->doc;
737 top = xmlDocGetRootElement(doc);
740 if (doc != NULL && top == child) {
759 sizeof(buffer)) > 0) {
762 crm_trace(
"Deleting %s %p from %p", buffer, child, doc);
765 deleted_obj->
path = strdup(buffer);
769 if (child->type == XML_COMMENT_NODE) {
792 free_xml_with_position(child, -1);
799 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
801 xmlDocSetRootElement(doc, copy);
802 xmlSetTreeDoc(copy, doc);
807 log_xmllib_err(
void *ctx,
const char *fmt, ...)
812 log_xmllib_err(
void *ctx, const
char *fmt, ...)
815 static struct qb_log_callsite *xml_error_cs = NULL;
817 if (xml_error_cs == NULL) {
818 xml_error_cs = qb_log_callsite_get(
823 if (xml_error_cs && xml_error_cs->targets) {
825 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__,
"xml library error",
827 "XML Error: ", fmt, ap);
838 xmlDocPtr output = NULL;
839 xmlParserCtxtPtr ctxt = NULL;
840 xmlErrorPtr last_error = NULL;
843 crm_err(
"Can't parse NULL input");
848 ctxt = xmlNewParserCtxt();
851 xmlCtxtResetLastError(ctxt);
852 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
853 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
856 xml = xmlDocGetRootElement(output);
858 last_error = xmlCtxtGetLastError(ctxt);
859 if (last_error && last_error->code != XML_ERR_OK) {
865 crm_warn(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
866 last_error->domain, last_error->level, last_error->code, last_error->message);
868 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
871 }
else if (last_error->code != XML_ERR_DOCUMENT_END) {
872 crm_err(
"Couldn't%s parse %d chars: %s", xml ?
" fully" :
"", (
int)strlen(input),
879 int len = strlen(input);
883 crm_warn(
"Parse error[+%.3d]: %.80s", lpc, input+lpc);
891 xmlFreeParserCtxt(ctxt);
898 size_t data_length = 0;
899 size_t read_chars = 0;
901 char *xml_buffer = NULL;
902 xmlNode *xml_obj = NULL;
908 data_length += read_chars;
911 if (data_length == 0) {
912 crm_warn(
"No XML supplied on stdin");
917 xml_buffer[data_length] =
'\0';
926 decompress_file(
const char *filename)
932 size_t length = 0, read_len = 0;
934 BZFILE *bz_file = NULL;
935 FILE *input = fopen(filename,
"r");
938 crm_perror(LOG_ERR,
"Could not open %s for reading", filename);
942 bz_file = BZ2_bzReadOpen(&
rc, input, 0, 0, NULL, 0);
944 crm_err(
"Could not prepare to read compressed %s: %s " 946 BZ2_bzReadClose(&
rc, bz_file);
953 while (
rc == BZ_OK) {
957 crm_trace(
"Read %ld bytes from file: %d", (
long)read_len,
rc);
959 if (
rc == BZ_OK ||
rc == BZ_STREAM_END) {
964 buffer[length] =
'\0';
966 if (
rc != BZ_STREAM_END) {
967 crm_err(
"Could not read compressed %s: %s " 973 BZ2_bzReadClose(&
rc, bz_file);
977 crm_err(
"Could not read compressed %s: not built with bzlib support",
992 xmlNode *iter = xml->children;
995 xmlNode *next = iter->next;
997 switch (iter->type) {
1003 case XML_ELEMENT_NODE:
1020 xmlNode *xml = NULL;
1021 xmlDocPtr output = NULL;
1022 gboolean uncompressed = TRUE;
1023 xmlParserCtxtPtr ctxt = NULL;
1024 xmlErrorPtr last_error = NULL;
1027 ctxt = xmlNewParserCtxt();
1030 xmlCtxtResetLastError(ctxt);
1031 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1037 if (filename == NULL) {
1039 output = xmlCtxtReadFd(ctxt, STDIN_FILENO,
"unknown.xml", NULL,
1042 }
else if (uncompressed) {
1046 char *input = decompress_file(filename);
1048 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
1053 if (output && (xml = xmlDocGetRootElement(output))) {
1057 last_error = xmlCtxtGetLastError(ctxt);
1058 if (last_error && last_error->code != XML_ERR_OK) {
1064 crm_err(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
1065 last_error->domain, last_error->level, last_error->code, last_error->message);
1067 if (last_error && last_error->code != XML_ERR_OK) {
1068 crm_err(
"Couldn't%s parse %s", xml ?
" fully" :
"", filename);
1075 xmlFreeParserCtxt(ctxt);
1093 now_str ? now_str :
"Could not determine current time");
1106 for (c =
id; *c; ++c) {
1131 va_start(ap, format);
1132 len = vasprintf(&
id, format, ap);
1154 write_xml_stream(xmlNode *xml_node,
const char *filename, FILE *stream,
1155 bool compress,
unsigned int *nbytes)
1158 char *buffer = NULL;
1171 unsigned int in = 0;
1172 BZFILE *bz_file = NULL;
1175 bz_file = BZ2_bzWriteOpen(&
rc, stream, 5, 0, 30);
1177 crm_warn(
"Not compressing %s: could not prepare file stream: %s " 1180 BZ2_bzWrite(&
rc, bz_file, buffer, strlen(buffer));
1182 crm_warn(
"Not compressing %s: could not compress data: %s " 1183 CRM_XS " bzerror=%d errno=%d",
1189 BZ2_bzWriteClose(&
rc, bz_file, 0, &in, nbytes);
1191 crm_warn(
"Not compressing %s: could not write compressed data: %s " 1192 CRM_XS " bzerror=%d errno=%d",
1196 crm_trace(
"Compressed XML for %s from %u bytes to %u",
1197 filename, in, *nbytes);
1202 crm_warn(
"Not compressing %s: not built with bzlib support", filename);
1207 rc = fprintf(stream,
"%s", buffer);
1212 *nbytes = (
unsigned int)
rc;
1219 if (fflush(stream) != 0) {
1221 crm_perror(LOG_ERR,
"flushing %s", filename);
1225 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1227 crm_perror(LOG_ERR,
"synchronizing %s", filename);
1232 crm_trace(
"Saved %d bytes to %s as XML", *nbytes, filename);
1249 write_xml_fd(xmlNode * xml_node,
const char *filename,
int fd, gboolean compress)
1251 FILE *stream = NULL;
1252 unsigned int nbytes = 0;
1255 CRM_CHECK(xml_node && (fd > 0),
return -EINVAL);
1256 stream = fdopen(fd,
"w");
1257 if (stream == NULL) {
1260 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1264 return (
int) nbytes;
1279 FILE *stream = NULL;
1280 unsigned int nbytes = 0;
1283 CRM_CHECK(xml_node && filename,
return -EINVAL);
1284 stream = fopen(filename,
"w");
1285 if (stream == NULL) {
1288 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1292 return (
int) nbytes;
1297 replace_text(
char *text,
int start,
int *length,
const char *replace)
1300 int offset = strlen(replace) - 1;
1303 text = pcmk__realloc(text, *length);
1305 for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1306 text[lpc] = text[lpc - offset];
1309 memcpy(text + start, replace, offset + 1);
1318 int length = 1 + strlen(text);
1319 char *copy = strdup(text);
1336 for (index = 0; index < length; index++) {
1337 switch (copy[index]) {
1341 copy = replace_text(copy, index, &length,
"<");
1345 copy = replace_text(copy, index, &length,
">");
1349 copy = replace_text(copy, index, &length,
""");
1353 copy = replace_text(copy, index, &length,
"'");
1357 copy = replace_text(copy, index, &length,
"&");
1362 copy = replace_text(copy, index, &length,
" ");
1367 copy = replace_text(copy, index, &length,
"\\n");
1371 copy = replace_text(copy, index, &length,
"\\r");
1381 if(copy[index] <
' ' || copy[index] >
'~') {
1385 copy = replace_text(copy, index, &length, replace);
1399 dump_xml_attr(xmlAttrPtr attr,
int options,
char **buffer,
int *offset,
int *max)
1401 char *p_value = NULL;
1402 const char *p_name = NULL;
1406 if (attr == NULL || attr->children == NULL) {
1415 p_name = (
const char *)attr->name;
1417 buffer_print(*buffer, *max, *offset,
" %s=\"%s\"", p_name, p_value);
1423 pcmk__xe_log(
int log_level,
const char *file,
const char *
function,
int line,
1424 const char *prefix, xmlNode *
data,
int depth,
int options)
1428 const char *
name = NULL;
1429 const char *hidden = NULL;
1431 xmlNode *child = NULL;
1432 xmlAttrPtr pIter = NULL;
1441 char *buffer = NULL;
1443 insert_prefix(options, &buffer, &offset, &max, depth);
1445 if (
data->type == XML_COMMENT_NODE) {
1452 for (pIter = pcmk__first_xml_attr(
data); pIter != NULL; pIter = pIter->next) {
1454 const char *p_name = (
const char *)pIter->name;
1455 const char *p_value = pcmk__xml_attr_value(pIter);
1456 char *p_copy = NULL;
1460 }
else if (pcmk_any_flags_set(options,
1466 }
else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1467 p_copy = strdup(
"*****");
1473 buffer_print(buffer, max, offset,
" %s=\"%s\"", p_name, p_copy);
1488 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
1492 if(
data->type == XML_COMMENT_NODE) {
1502 for (child = pcmk__xml_first_child(
data); child != NULL;
1503 child = pcmk__xml_next(child)) {
1504 pcmk__xe_log(log_level, file,
function, line, prefix, child,
1511 char *buffer = NULL;
1513 insert_prefix(options, &buffer, &offset, &max, depth);
1516 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
1523 log_xml_changes(
int log_level,
const char *file,
const char *
function,
int line,
1524 const char *prefix, xmlNode *
data,
int depth,
int options)
1527 char *prefix_m = NULL;
1528 xmlNode *child = NULL;
1529 xmlAttrPtr pIter = NULL;
1537 prefix_m = strdup(prefix);
1547 char *spaces = calloc(80, 1);
1548 int s_count = 0, s_max = 80;
1549 char *prefix_del = NULL;
1550 char *prefix_moved = NULL;
1551 const char *
flags = prefix;
1553 insert_prefix(options, &spaces, &s_count, &s_max, depth);
1554 prefix_del = strdup(prefix);
1555 prefix_del[0] =
'-';
1556 prefix_del[1] =
'-';
1557 prefix_moved = strdup(prefix);
1558 prefix_moved[1] =
'~';
1561 flags = prefix_moved;
1569 for (pIter = pcmk__first_xml_attr(
data); pIter != NULL; pIter = pIter->next) {
1570 const char *aname = (
const char*)pIter->name;
1572 p = pIter->_private;
1577 "%s %s @%s=%s",
flags, spaces, aname, value);
1589 flags = prefix_moved;
1595 "%s %s @%s=%s",
flags, spaces, aname, value);
1602 for (child = pcmk__xml_first_child(
data); child != NULL;
1603 child = pcmk__xml_next(child)) {
1604 log_xml_changes(log_level, file,
function, line, prefix, child,
1605 depth + 1, options);
1612 for (child = pcmk__xml_first_child(
data); child != NULL;
1613 child = pcmk__xml_next(child)) {
1614 log_xml_changes(log_level, file,
function, line, prefix, child,
1615 depth + 1, options);
1625 const char *prefix, xmlNode *
data,
int depth,
int options)
1627 xmlNode *a_child = NULL;
1629 char *prefix_m = NULL;
1635 if (prefix == NULL) {
1642 "No data to dump as XML");
1647 log_xml_changes(log_level, file,
function, line, prefix,
data, depth,
1656 prefix_m = strdup(prefix);
1663 prefix_m = strdup(prefix);
1672 for (a_child = pcmk__xml_first_child(
data); a_child != NULL;
1673 a_child = pcmk__xml_next(a_child)) {
1674 log_data_element(log_level, file,
function, line, prefix, a_child, depth + 1, options);
1685 dump_filtered_xml(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max)
1687 xmlAttrPtr xIter = NULL;
1689 for (xIter = pcmk__first_xml_attr(
data); xIter != NULL; xIter = xIter->next) {
1691 dump_xml_attr(xIter, options, buffer, offset, max);
1697 dump_xml_element(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1699 const char *
name = NULL;
1710 if (*buffer == NULL) {
1718 insert_prefix(options, buffer, offset, max, depth);
1722 dump_filtered_xml(
data, options, buffer, offset, max);
1725 xmlAttrPtr xIter = NULL;
1727 for (xIter = pcmk__first_xml_attr(
data); xIter != NULL; xIter = xIter->next) {
1728 dump_xml_attr(xIter, options, buffer, offset, max);
1732 if (
data->children == NULL) {
1743 if (
data->children) {
1744 xmlNode *xChild = NULL;
1745 for(xChild =
data->children; xChild != NULL; xChild = xChild->next) {
1749 insert_prefix(options, buffer, offset, max, depth);
1759 dump_xml_text(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1770 if (*buffer == NULL) {
1775 insert_prefix(options, buffer, offset, max, depth);
1785 dump_xml_cdata(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1796 if (*buffer == NULL) {
1801 insert_prefix(options, buffer, offset, max, depth);
1813 dump_xml_comment(xmlNode *
data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1824 if (*buffer == NULL) {
1829 insert_prefix(options, buffer, offset, max, depth);
1840 #define PCMK__XMLDUMP_STATS 0 1855 int *max,
int depth)
1872 #if (PCMK__XMLDUMP_STATS - 0) 1873 time_t next,
new = time(NULL);
1876 xmlOutputBuffer *xml_buffer;
1882 xml_buffer = xmlAllocOutputBuffer(NULL);
1895 xmlNodeDumpOutput(xml_buffer, doc,
data, 0,
1898 (void) xmlOutputBufferWrite(xml_buffer,
sizeof(
"\n") - 1,
"\n");
1899 if (xml_buffer->buffer != NULL) {
1901 (
char *) xmlBufContent(xml_buffer->buffer));
1904 #if (PCMK__XMLDUMP_STATS - 0) 1906 if ((now + 1) < next) {
1908 crm_err(
"xmlNodeDump() -> %dbytes took %ds", *max, next - now);
1913 (void) xmlOutputBufferClose(xml_buffer);
1917 switch(
data->type) {
1918 case XML_ELEMENT_NODE:
1920 dump_xml_element(
data, options, buffer, offset, max, depth);
1925 dump_xml_text(
data, options, buffer, offset, max, depth);
1928 case XML_COMMENT_NODE:
1929 dump_xml_comment(
data, options, buffer, offset, max, depth);
1931 case XML_CDATA_SECTION_NODE:
1932 dump_xml_cdata(
data, options, buffer, offset, max, depth);
1979 char *buffer = NULL;
1980 int offset = 0, max = 0;
1984 &buffer, &offset, &max, 0);
1991 char *buffer = NULL;
1992 int offset = 0, max = 0;
2002 char *buffer = NULL;
2003 int offset = 0, max = 0;
2012 if (xml_root != NULL && xml_root->children != NULL) {
2042 if (filename == NULL) {
2050 crm_info(
"Saving %s to %s", desc, filename);
2065 for (xmlAttr *attr = pcmk__first_xml_attr(xml); attr; attr = attr->next) {
2080 mark_attr_deleted(xmlNode *new_xml,
const char *element,
const char *attr_name,
2081 const char *old_value)
2084 xmlAttr *attr = NULL;
2100 crm_trace(
"XML attribute %s=%s was removed from %s",
2101 attr_name, old_value, element);
2109 mark_attr_changed(xmlNode *new_xml,
const char *element,
const char *attr_name,
2110 const char *old_value)
2114 crm_trace(
"XML attribute %s was changed from '%s' to '%s' in %s",
2115 attr_name, old_value, vcopy, element);
2130 mark_attr_moved(xmlNode *new_xml,
const char *element, xmlAttr *old_attr,
2131 xmlAttr *new_attr,
int p_old,
int p_new)
2135 crm_trace(
"XML attribute %s moved from position %d to %d in %s",
2136 old_attr->name, p_old, p_new, element);
2139 mark_xml_node_dirty(new_xml);
2144 p = (p_old > p_new)? old_attr->_private : new_attr->_private;
2153 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2155 xmlAttr *attr_iter = pcmk__first_xml_attr(old_xml);
2157 while (attr_iter != NULL) {
2158 xmlAttr *old_attr = attr_iter;
2159 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2160 const char *
name = (
const char *) attr_iter->name;
2163 attr_iter = attr_iter->next;
2164 if (new_attr == NULL) {
2165 mark_attr_deleted(new_xml, (
const char *) old_xml->name,
name,
2177 if (strcmp(new_value, old_value) != 0) {
2178 mark_attr_changed(new_xml, (
const char *) old_xml->name,
name,
2181 }
else if ((old_pos != new_pos)
2183 mark_attr_moved(new_xml, (
const char *) old_xml->name,
2184 old_attr, new_attr, old_pos, new_pos);
2195 mark_created_attrs(xmlNode *new_xml)
2197 xmlAttr *attr_iter = pcmk__first_xml_attr(new_xml);
2199 while (attr_iter != NULL) {
2200 xmlAttr *new_attr = attr_iter;
2203 attr_iter = attr_iter->next;
2205 const char *attr_name = (
const char *) new_attr->name;
2207 crm_trace(
"Created new attribute %s=%s in %s",
2218 xmlUnsetProp(new_xml, new_attr->name);
2229 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2232 xml_diff_old_attrs(old_xml, new_xml);
2233 mark_created_attrs(new_xml);
2246 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2252 reset_xml_node_flags(candidate);
2258 free_xml_with_position(candidate,
2267 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2268 int p_old,
int p_new)
2272 crm_trace(
"Child element %s with id='%s' moved from position %d to %d under %s",
2273 new_child->name, (
ID(new_child)?
ID(new_child) :
"<no id>"),
2274 p_old, p_new, new_parent->name);
2275 mark_xml_node_dirty(new_parent);
2278 if (p_old > p_new) {
2279 p = old_child->_private;
2281 p = new_child->_private;
2288 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml,
bool check_top)
2290 xmlNode *cIter = NULL;
2294 if (old_xml == NULL) {
2300 p = new_xml->_private;
2309 xml_diff_attrs(old_xml, new_xml);
2312 for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2313 xmlNode *old_child = cIter;
2316 cIter = pcmk__xml_next(cIter);
2318 mark_xml_changes(old_child, new_child, TRUE);
2321 mark_child_deleted(old_child, new_xml);
2326 for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2327 xmlNode *new_child = cIter;
2330 cIter = pcmk__xml_next(cIter);
2331 if(old_child == NULL) {
2333 p = new_child->_private;
2335 mark_xml_changes(old_child, new_child, TRUE);
2342 if(p_old != p_new) {
2343 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2367 mark_xml_changes(old_xml, new_xml, FALSE);
2373 xmlNode *cIter = NULL;
2374 xmlAttrPtr pIter = NULL;
2375 gboolean can_prune = TRUE;
2376 const char *
name = crm_element_name(xml_node);
2383 for (pIter = pcmk__first_xml_attr(xml_node); pIter != NULL; pIter = pIter->next) {
2384 const char *p_name = (
const char *)pIter->name;
2392 cIter = pcmk__xml_first_child(xml_node);
2394 xmlNode *child = cIter;
2396 cIter = pcmk__xml_next(cIter);
2417 xmlNode *a_child = NULL;
2420 CRM_CHECK(search_comment->type == XML_COMMENT_NODE,
return NULL);
2422 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2423 a_child = pcmk__xml_next(a_child)) {
2428 if (offset < search_offset) {
2431 }
else if (offset > search_offset) {
2440 if (a_child->type == XML_COMMENT_NODE
2441 && pcmk__str_eq((
const char *)a_child->content, (
const char *)search_comment->content,
pcmk__str_casei)) {
2467 CRM_CHECK(update->type == XML_COMMENT_NODE,
return);
2476 }
else if (!pcmk__str_eq((
const char *)
target->content, (
const char *)update->content,
pcmk__str_casei)) {
2477 xmlFree(
target->content);
2478 target->content = xmlStrdup(update->content);
2498 xmlNode *a_child = NULL;
2499 const char *object_name = NULL,
2500 *object_href = NULL,
2501 *object_href_val = NULL;
2503 #if XML_PARSER_DEBUG 2510 if (update->type == XML_COMMENT_NODE) {
2515 object_name = crm_element_name(update);
2516 object_href_val =
ID(update);
2517 if (object_href_val != NULL) {
2529 object_href, object_href_val);
2535 #if XML_PARSER_DEBUG 2537 object_href ?
" " :
"",
2538 object_href ? object_href :
"",
2539 object_href ?
"=" :
"",
2540 object_href ? object_href_val :
"");
2544 object_href ?
" " :
"",
2545 object_href ? object_href :
"",
2546 object_href ?
"=" :
"",
2547 object_href ? object_href_val :
"");
2551 CRM_CHECK(pcmk__str_eq(crm_element_name(
target), crm_element_name(update),
2555 if (as_diff == FALSE) {
2561 xmlAttrPtr pIter = NULL;
2563 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
2564 const char *p_name = (
const char *)pIter->name;
2565 const char *p_value = pcmk__xml_attr_value(pIter);
2573 for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2574 a_child = pcmk__xml_next(a_child)) {
2575 #if XML_PARSER_DEBUG 2577 object_href ?
" " :
"",
2578 object_href ? object_href :
"",
2579 object_href ?
"=" :
"",
2580 object_href ? object_href_val :
"");
2585 #if XML_PARSER_DEBUG 2587 object_href ?
" " :
"",
2588 object_href ? object_href :
"",
2589 object_href ?
"=" :
"",
2590 object_href ? object_href_val :
"");
2597 gboolean can_update = TRUE;
2598 xmlNode *child_of_child = NULL;
2601 CRM_CHECK(to_update != NULL,
return FALSE);
2603 if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child),
pcmk__str_casei)) {
2609 }
else if (can_update) {
2610 #if XML_PARSER_DEBUG 2616 for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2617 child_of_child = pcmk__xml_next(child_of_child)) {
2630 const char *tag,
const char *field,
const char *value, gboolean search_matches)
2632 int match_found = 0;
2635 CRM_CHECK(children != NULL,
return FALSE);
2637 if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root),
pcmk__str_casei)) {
2642 if (*children == NULL) {
2649 if (search_matches || match_found == 0) {
2650 xmlNode *child = NULL;
2652 for (child = pcmk__xml_first_child(root); child != NULL;
2653 child = pcmk__xml_next(child)) {
2654 match_found +=
find_xml_children(children, child, tag, field, value, search_matches);
2664 gboolean can_delete = FALSE;
2665 xmlNode *child_of_child = NULL;
2667 const char *up_id = NULL;
2668 const char *child_id = NULL;
2669 const char *right_val = NULL;
2672 CRM_CHECK(update != NULL,
return FALSE);
2675 child_id =
ID(child);
2677 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2680 if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child),
pcmk__str_casei)) {
2683 if (can_delete && delete_only) {
2684 xmlAttrPtr pIter = NULL;
2686 for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
2687 const char *p_name = (
const char *)pIter->name;
2688 const char *p_value = pcmk__xml_attr_value(pIter);
2697 if (can_delete && parent != NULL) {
2699 if (delete_only || update == NULL) {
2704 xmlDoc *doc = tmp->doc;
2705 xmlNode *old = NULL;
2708 old = xmlReplaceNode(child, tmp);
2716 xmlDocSetRootElement(doc, old);
2722 }
else if (can_delete) {
2727 child_of_child = pcmk__xml_first_child(child);
2728 while (child_of_child) {
2729 xmlNode *next = pcmk__xml_next(child_of_child);
2735 child_of_child = NULL;
2737 child_of_child = next;
2747 xmlNode *child = NULL;
2748 GSList *nvpairs = NULL;
2749 xmlNode *result = NULL;
2750 const char *
name = NULL;
2754 name = crm_element_name(input);
2763 for (child = pcmk__xml_first_child(input); child != NULL;
2764 child = pcmk__xml_next(child)) {
2779 xmlNode *match = NULL;
2781 for (match = pcmk__xe_first_child(parent); match != NULL;
2782 match = pcmk__xe_next(match)) {
2805 xmlNode *match = pcmk__xe_next(sibling);
2806 const char *
name = crm_element_name(sibling);
2808 while (match != NULL) {
2809 if (!strcmp(crm_element_name(match),
name)) {
2812 match = pcmk__xe_next(match);
2820 static bool init = TRUE;
2829 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2832 xmlDeregisterNodeDefault(free_private_data);
2833 xmlRegisterNodeDefault(new_private_data);
2842 crm_info(
"Cleaning up memory from libxml2");
2847 #define XPATH_MAX 512 2852 const char *tag = NULL;
2853 const char *ref = NULL;
2854 xmlNode *result = input;
2856 if (result == NULL) {
2859 }
else if (top == NULL) {
2863 tag = crm_element_name(result);
2870 if (result == NULL) {
2871 char *nodePath = (
char *)xmlGetNodePath(top);
2873 crm_err(
"No match for %s found in %s: Invalid configuration", xpath_string,
2891 static const char *base = NULL;
2895 base = getenv(
"PCMK_schema_directory");
2897 if (pcmk__str_empty(base)) {
2911 crm_err(
"XML artefact family specified as %u not recognized", ns);
2931 crm_err(
"XML artefact family specified as %u not recognized", ns);
2940 xmlNode *
find_entity(xmlNode *parent,
const char *node_name,
const char *
id);
#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)
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__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)
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__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
#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)
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)
#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)
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
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)