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 marked_as_deleted(xmlAttrPtr a,
void *user_data)
346 accept_attr_deletions(xmlNode *xml)
355 for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
356 cIter = pcmk__xml_next(cIter)) {
357 accept_attr_deletions(cIter);
374 if (needle->type == XML_COMMENT_NODE) {
378 const char *
id =
ID(needle);
379 const char *attr = (
id == NULL)? NULL :
XML_ATTR_ID;
381 return pcmk__xe_match(haystack, crm_element_name(needle), attr,
id);
398 doc = xml->doc->_private;
403 for(gIter = doc->
deleted_objs; gIter; gIter = gIter->next) {
430 crm_trace(
"Accepting changes to %p", xml);
431 doc = xml->doc->_private;
432 top = xmlDocGetRootElement(xml->doc);
434 reset_xml_private_data(xml->doc->_private);
442 accept_attr_deletions(top);
448 xmlNode *a_child = NULL;
449 const char *
name =
"NULL";
452 name = crm_element_name(root);
455 if (search_path == NULL) {
460 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
461 a_child = pcmk__xml_next(a_child)) {
462 if (strcmp((
const char *)a_child->name, search_path) == 0) {
469 crm_warn(
"Could not find %s in %s.", search_path, name);
470 }
else if (root != NULL) {
471 crm_trace(
"Could not find %s in %s.", search_path, name);
473 crm_trace(
"Could not find %s in <NULL>.", search_path);
479 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \
497 const char *attr_n,
const char *attr_v)
500 CRM_CHECK(attr_n == NULL || attr_v != NULL,
return NULL);
502 for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
503 child = pcmk__xml_next(child)) {
504 if (pcmk__str_eq(node_name, (
const char *) (child->name),
506 && ((attr_n == NULL) ||
attr_matches(child, attr_n, attr_v))) {
510 crm_trace(
"XML child node <%s%s%s%s%s> not found in %s",
511 (node_name? node_name :
"(any)"),
513 (attr_n? attr_n :
""),
515 (attr_n? attr_v :
""),
516 crm_element_name(parent));
524 crm_warn(
"No node to copy properties from");
526 }
else if (target == NULL) {
527 crm_err(
"No node to copy properties into");
530 for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
531 const char *p_name = (
const char *) a->name;
532 const char *p_value = pcmk__xml_attr_value(a);
545 xmlNode *child = NULL;
547 for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
548 const char *p_name = (
const char *) a->name;
549 const char *p_value = pcmk__xml_attr_value(a);
553 for (child = pcmk__xml_first_child(target); child != NULL;
554 child = pcmk__xml_next(child)) {
567 const char *old_value = NULL;
569 if (value == NULL || name == NULL) {
575 if (old_value == NULL) {
579 }
else if (strstr(value, name) != value) {
583 name_len = strlen(name);
584 value_len = strlen(value);
585 if (value_len < (name_len + 2)
586 || value[name_len] !=
'+' || (value[name_len + 1] !=
'+' && value[name_len + 1] !=
'=')) {
593 if (old_value != value) {
597 if (value[name_len + 1] !=
'+') {
598 const char *offset_s = value + (name_len + 2);
612 if (old_value == value) {
631 bool (*match)(xmlAttrPtr,
void *),
634 xmlAttrPtr next = NULL;
636 for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
638 if ((match == NULL) || match(a, user_data)) {
640 crm_trace(
"ACLs prevent removal of attributes (%s and "
641 "possibly others) from %s element",
642 (
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)) {
702 CRM_CHECK(name != NULL && name[0] == 0,
return NULL);
706 if (parent == NULL) {
708 node = xmlNewDocRawNode(doc, NULL, (
pcmkXmlStr) name, NULL);
709 xmlDocSetRootElement(doc, node);
713 node = xmlNewDocRawNode(doc, NULL, (
pcmkXmlStr) name, NULL);
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) {
825 free_xml_with_position(child, -1);
832 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
834 xmlDocSetRootElement(doc, copy);
835 xmlSetTreeDoc(copy, doc);
840 log_xmllib_err(
void *ctx,
const char *fmt, ...)
845 log_xmllib_err(
void *ctx, const
char *fmt, ...)
848 static struct qb_log_callsite *xml_error_cs = NULL;
850 if (xml_error_cs == NULL) {
851 xml_error_cs = qb_log_callsite_get(
856 if (xml_error_cs && xml_error_cs->targets) {
858 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__,
"xml library error",
860 "XML Error: ", fmt, ap);
871 xmlDocPtr output = NULL;
872 xmlParserCtxtPtr ctxt = NULL;
873 xmlErrorPtr last_error = NULL;
876 crm_err(
"Can't parse NULL input");
881 ctxt = xmlNewParserCtxt();
884 xmlCtxtResetLastError(ctxt);
885 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
886 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
889 xml = xmlDocGetRootElement(output);
891 last_error = xmlCtxtGetLastError(ctxt);
892 if (last_error && last_error->code != XML_ERR_OK) {
898 crm_warn(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
899 last_error->domain, last_error->level, last_error->code, last_error->message);
901 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
904 }
else if (last_error->code != XML_ERR_DOCUMENT_END) {
905 crm_err(
"Couldn't%s parse %d chars: %s", xml ?
" fully" :
"", (
int)strlen(input),
912 int len = strlen(input);
916 crm_warn(
"Parse error[+%.3d]: %.80s", lpc, input+lpc);
924 xmlFreeParserCtxt(ctxt);
931 size_t data_length = 0;
932 size_t read_chars = 0;
934 char *xml_buffer = NULL;
935 xmlNode *xml_obj = NULL;
941 data_length += read_chars;
944 if (data_length == 0) {
945 crm_warn(
"No XML supplied on stdin");
950 xml_buffer[data_length] =
'\0';
959 decompress_file(
const char *filename)
963 size_t length = 0, read_len = 0;
964 BZFILE *bz_file = NULL;
965 FILE *input = fopen(filename,
"r");
968 crm_perror(LOG_ERR,
"Could not open %s for reading", filename);
972 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
974 crm_err(
"Could not prepare to read compressed %s: %s "
976 BZ2_bzReadClose(&rc, bz_file);
983 while (rc == BZ_OK) {
987 crm_trace(
"Read %ld bytes from file: %d", (
long)read_len, rc);
989 if (rc == BZ_OK || rc == BZ_STREAM_END) {
994 buffer[length] =
'\0';
996 if (rc != BZ_STREAM_END) {
997 crm_err(
"Could not read compressed %s: %s "
1003 BZ2_bzReadClose(&rc, bz_file);
1017 xmlNode *iter = xml->children;
1020 xmlNode *next = iter->next;
1022 switch (iter->type) {
1028 case XML_ELEMENT_NODE:
1045 xmlNode *xml = NULL;
1046 xmlDocPtr output = NULL;
1047 gboolean uncompressed = TRUE;
1048 xmlParserCtxtPtr ctxt = NULL;
1049 xmlErrorPtr last_error = NULL;
1052 ctxt = xmlNewParserCtxt();
1055 xmlCtxtResetLastError(ctxt);
1056 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1062 if (filename == NULL || !strcmp(filename,
"-")) {
1064 output = xmlCtxtReadFd(ctxt, STDIN_FILENO,
"unknown.xml", NULL,
1067 }
else if (uncompressed) {
1071 char *input = decompress_file(filename);
1073 output = xmlCtxtReadDoc(ctxt, (
pcmkXmlStr) input, NULL, NULL,
1078 if (output && (xml = xmlDocGetRootElement(output))) {
1082 last_error = xmlCtxtGetLastError(ctxt);
1083 if (last_error && last_error->code != XML_ERR_OK) {
1089 crm_err(
"Parsing failed (domain=%d, level=%d, code=%d): %s",
1090 last_error->domain, last_error->level, last_error->code, last_error->message);
1092 if (last_error && last_error->code != XML_ERR_OK) {
1093 crm_err(
"Couldn't%s parse %s", xml ?
" fully" :
"", filename);
1100 xmlFreeParserCtxt(ctxt);
1118 now_str ? now_str :
"Could not determine current time");
1131 for (c =
id; *c; ++c) {
1156 va_start(ap, format);
1157 len = vasprintf(&
id, format, ap);
1179 write_xml_stream(xmlNode *xml_node,
const char *filename, FILE *stream,
1180 bool compress,
unsigned int *nbytes)
1183 char *buffer = NULL;
1195 unsigned int in = 0;
1196 BZFILE *bz_file = NULL;
1199 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1201 crm_warn(
"Not compressing %s: could not prepare file stream: %s "
1204 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1206 crm_warn(
"Not compressing %s: could not compress data: %s "
1207 CRM_XS " bzerror=%d errno=%d",
1213 BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1215 crm_warn(
"Not compressing %s: could not write compressed data: %s "
1216 CRM_XS " bzerror=%d errno=%d",
1220 crm_trace(
"Compressed XML for %s from %u bytes to %u",
1221 filename, in, *nbytes);
1228 rc = fprintf(stream,
"%s", buffer);
1233 *nbytes = (
unsigned int) rc;
1240 if (fflush(stream) != 0) {
1242 crm_perror(LOG_ERR,
"flushing %s", filename);
1246 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1248 crm_perror(LOG_ERR,
"synchronizing %s", filename);
1253 crm_trace(
"Saved %d bytes to %s as XML", *nbytes, filename);
1270 write_xml_fd(xmlNode * xml_node,
const char *filename,
int fd, gboolean compress)
1272 FILE *stream = NULL;
1273 unsigned int nbytes = 0;
1276 CRM_CHECK(xml_node && (fd > 0),
return -EINVAL);
1277 stream = fdopen(fd,
"w");
1278 if (stream == NULL) {
1281 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1285 return (
int) nbytes;
1300 FILE *stream = NULL;
1301 unsigned int nbytes = 0;
1304 CRM_CHECK(xml_node && filename,
return -EINVAL);
1305 stream = fopen(filename,
"w");
1306 if (stream == NULL) {
1309 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1313 return (
int) nbytes;
1318 replace_text(
char *text,
int start,
int *length,
const char *replace)
1321 int offset = strlen(replace) - 1;
1324 text = pcmk__realloc(text, *length);
1326 for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1327 text[lpc] = text[lpc - offset];
1330 memcpy(text + start, replace, offset + 1);
1339 int length = 1 + strlen(text);
1340 char *copy = strdup(text);
1357 for (index = 0; index < length; index++) {
1358 switch (copy[index]) {
1362 copy = replace_text(copy, index, &length,
"<");
1366 copy = replace_text(copy, index, &length,
">");
1370 copy = replace_text(copy, index, &length,
""");
1374 copy = replace_text(copy, index, &length,
"'");
1378 copy = replace_text(copy, index, &length,
"&");
1383 copy = replace_text(copy, index, &length,
" ");
1388 copy = replace_text(copy, index, &length,
"\\n");
1392 copy = replace_text(copy, index, &length,
"\\r");
1402 if(copy[index] <
' ' || copy[index] >
'~') {
1406 copy = replace_text(copy, index, &length, replace);
1420 dump_xml_attr(xmlAttrPtr attr,
int options,
char **buffer,
int *offset,
int *max)
1422 char *p_value = NULL;
1423 const char *p_name = NULL;
1427 if (attr == NULL || attr->children == NULL) {
1436 p_name = (
const char *)attr->name;
1438 buffer_print(*buffer, *max, *offset,
" %s=\"%s\"", p_name, p_value);
1444 pcmk__xe_log(
int log_level,
const char *file,
const char *
function,
int line,
1445 const char *prefix, xmlNode *
data,
int depth,
int options)
1449 const char *
name = NULL;
1450 const char *hidden = NULL;
1452 xmlNode *child = NULL;
1454 if ((data == NULL) || (log_level ==
LOG_NEVER)) {
1458 name = crm_element_name(data);
1461 char *buffer = NULL;
1463 insert_prefix(options, &buffer, &offset, &max, depth);
1465 if (data->type == XML_COMMENT_NODE) {
1466 buffer_print(buffer, max, offset,
"<!--%s-->", data->content);
1472 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL;
1476 const char *p_name = (
const char *) a->name;
1477 const char *p_value = pcmk__xml_attr_value(a);
1478 char *p_copy = NULL;
1482 }
else if (pcmk_any_flags_set(options,
1488 }
else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1489 p_copy = strdup(
"*****");
1495 buffer_print(buffer, max, offset,
" %s=\"%s\"", p_name, p_copy);
1510 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
1514 if(data->type == XML_COMMENT_NODE) {
1524 for (child = pcmk__xml_first_child(data); child != NULL;
1525 child = pcmk__xml_next(child)) {
1526 pcmk__xe_log(log_level, file,
function, line, prefix, child,
1533 char *buffer = NULL;
1535 insert_prefix(options, &buffer, &offset, &max, depth);
1538 do_crm_log_alias(log_level, file,
function, line,
"%s %s", prefix, buffer);
1545 log_xml_changes(
int log_level,
const char *file,
const char *
function,
int line,
1546 const char *prefix, xmlNode *
data,
int depth,
int options)
1549 char *prefix_m = NULL;
1550 xmlNode *child = NULL;
1552 if ((data == NULL) || (log_level ==
LOG_NEVER)) {
1558 prefix_m = strdup(prefix);
1563 pcmk__xe_log(log_level, file,
function, line, prefix_m, data, depth,
1568 char *spaces = calloc(80, 1);
1569 int s_count = 0, s_max = 80;
1570 char *prefix_del = NULL;
1571 char *prefix_moved = NULL;
1572 const char *
flags = prefix;
1574 insert_prefix(options, &spaces, &s_count, &s_max, depth);
1575 prefix_del = strdup(prefix);
1576 prefix_del[0] =
'-';
1577 prefix_del[1] =
'-';
1578 prefix_moved = strdup(prefix);
1579 prefix_moved[1] =
'~';
1582 flags = prefix_moved;
1587 pcmk__xe_log(log_level, file,
function, line, flags, data, depth,
1590 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1591 const char *aname = (
const char*) a->name;
1598 "%s %s @%s=%s", flags, spaces, aname, value);
1610 flags = prefix_moved;
1616 "%s %s @%s=%s", flags, spaces, aname, value);
1623 for (child = pcmk__xml_first_child(data); child != NULL;
1624 child = pcmk__xml_next(child)) {
1625 log_xml_changes(log_level, file,
function, line, prefix, child,
1626 depth + 1, options);
1629 pcmk__xe_log(log_level, file,
function, line, prefix, data, depth,
1633 for (child = pcmk__xml_first_child(data); child != NULL;
1634 child = pcmk__xml_next(child)) {
1635 log_xml_changes(log_level, file,
function, line, prefix, child,
1636 depth + 1, options);
1646 const char *prefix, xmlNode * data,
int depth,
int options)
1648 xmlNode *a_child = NULL;
1650 char *prefix_m = NULL;
1656 if (prefix == NULL) {
1663 "No data to dump as XML");
1668 log_xml_changes(log_level, file,
function, line, prefix, data, depth,
1677 prefix_m = strdup(prefix);
1684 prefix_m = strdup(prefix);
1693 for (a_child = pcmk__xml_first_child(data); a_child != NULL;
1694 a_child = pcmk__xml_next(a_child)) {
1695 log_data_element(log_level, file,
function, line, prefix, a_child, depth + 1, options);
1698 pcmk__xe_log(log_level, file,
function, line, prefix, data, depth,
1706 dump_filtered_xml(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max)
1708 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1710 dump_xml_attr(a, options, buffer, offset, max);
1716 dump_xml_element(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1718 const char *
name = NULL;
1729 if (*buffer == NULL) {
1734 name = crm_element_name(data);
1737 insert_prefix(options, buffer, offset, max, depth);
1741 dump_filtered_xml(data, options, buffer, offset, max);
1744 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1745 dump_xml_attr(a, options, buffer, offset, max);
1749 if (data->children == NULL) {
1760 if (data->children) {
1761 xmlNode *xChild = NULL;
1762 for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1766 insert_prefix(options, buffer, offset, max, depth);
1769 if (options & xml_log_option_formatted) {
1776 dump_xml_text(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1787 if (*buffer == NULL) {
1792 insert_prefix(options, buffer, offset, max, depth);
1794 buffer_print(*buffer, *max, *offset,
"%s", data->content);
1796 if (options & xml_log_option_formatted) {
1802 dump_xml_cdata(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1813 if (*buffer == NULL) {
1818 insert_prefix(options, buffer, offset, max, depth);
1821 buffer_print(*buffer, *max, *offset,
"%s", data->content);
1824 if (options & xml_log_option_formatted) {
1830 dump_xml_comment(xmlNode * data,
int options,
char **buffer,
int *offset,
int *max,
int depth)
1841 if (*buffer == NULL) {
1846 insert_prefix(options, buffer, offset, max, depth);
1849 buffer_print(*buffer, *max, *offset,
"%s", data->content);
1852 if (options & xml_log_option_formatted) {
1857 #define PCMK__XMLDUMP_STATS 0
1872 int *max,
int depth)
1880 if (!
pcmk_is_set(options, xml_log_option_filtered)
1889 #if (PCMK__XMLDUMP_STATS - 0)
1890 time_t next,
new = time(NULL);
1893 xmlOutputBuffer *xml_buffer;
1899 xml_buffer = xmlAllocOutputBuffer(NULL);
1912 xmlNodeDumpOutput(xml_buffer, doc, data, 0,
1913 (options & xml_log_option_formatted), NULL);
1915 (void) xmlOutputBufferWrite(xml_buffer,
sizeof(
"\n") - 1,
"\n");
1916 if (xml_buffer->buffer != NULL) {
1918 (
char *) xmlBufContent(xml_buffer->buffer));
1921 #if (PCMK__XMLDUMP_STATS - 0)
1923 if ((now + 1) < next) {
1925 crm_err(
"xmlNodeDump() -> %dbytes took %ds", *max, next - now);
1930 (void) xmlOutputBufferClose(xml_buffer);
1934 switch(data->type) {
1935 case XML_ELEMENT_NODE:
1937 dump_xml_element(data, options, buffer, offset, max, depth);
1942 dump_xml_text(data, options, buffer, offset, max, depth);
1945 case XML_COMMENT_NODE:
1946 dump_xml_comment(data, options, buffer, offset, max, depth);
1948 case XML_CDATA_SECTION_NODE:
1949 dump_xml_cdata(data, options, buffer, offset, max, depth);
1952 crm_warn(
"Unhandled type: %d", data->type);
1996 char *buffer = NULL;
1997 int offset = 0, max = 0;
2001 &buffer, &offset, &max, 0);
2008 char *buffer = NULL;
2009 int offset = 0, max = 0;
2011 pcmk__xml2text(an_xml_node, xml_log_option_formatted, &buffer, &offset,
2019 char *buffer = NULL;
2020 int offset = 0, max = 0;
2029 if (xml_root != NULL && xml_root->children != NULL) {
2039 crm_trace(
"Cannot remove %s from %s", name, obj->name);
2044 xmlAttr *attr = xmlHasProp(obj, (
pcmkXmlStr) name);
2059 if (filename == NULL) {
2067 crm_info(
"Saving %s to %s", desc, filename);
2082 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2097 mark_attr_deleted(xmlNode *new_xml,
const char *element,
const char *attr_name,
2098 const char *old_value)
2101 xmlAttr *attr = NULL;
2117 crm_trace(
"XML attribute %s=%s was removed from %s",
2118 attr_name, old_value, element);
2126 mark_attr_changed(xmlNode *new_xml,
const char *element,
const char *attr_name,
2127 const char *old_value)
2131 crm_trace(
"XML attribute %s was changed from '%s' to '%s' in %s",
2132 attr_name, old_value, vcopy, element);
2147 mark_attr_moved(xmlNode *new_xml,
const char *element, xmlAttr *old_attr,
2148 xmlAttr *new_attr,
int p_old,
int p_new)
2152 crm_trace(
"XML attribute %s moved from position %d to %d in %s",
2153 old_attr->name, p_old, p_new, element);
2156 mark_xml_node_dirty(new_xml);
2161 p = (p_old > p_new)? old_attr->_private : new_attr->_private;
2170 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2172 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2174 while (attr_iter != NULL) {
2175 xmlAttr *old_attr = attr_iter;
2176 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2177 const char *name = (
const char *) attr_iter->name;
2180 attr_iter = attr_iter->next;
2181 if (new_attr == NULL) {
2182 mark_attr_deleted(new_xml, (
const char *) old_xml->name, name,
2194 if (strcmp(new_value, old_value) != 0) {
2195 mark_attr_changed(new_xml, (
const char *) old_xml->name, name,
2198 }
else if ((old_pos != new_pos)
2200 mark_attr_moved(new_xml, (
const char *) old_xml->name,
2201 old_attr, new_attr, old_pos, new_pos);
2212 mark_created_attrs(xmlNode *new_xml)
2214 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2216 while (attr_iter != NULL) {
2217 xmlAttr *new_attr = attr_iter;
2220 attr_iter = attr_iter->next;
2222 const char *attr_name = (
const char *) new_attr->name;
2224 crm_trace(
"Created new attribute %s=%s in %s",
2235 xmlUnsetProp(new_xml, new_attr->name);
2246 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2249 xml_diff_old_attrs(old_xml, new_xml);
2250 mark_created_attrs(new_xml);
2263 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2269 reset_xml_node_flags(candidate);
2275 free_xml_with_position(candidate,
2284 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2285 int p_old,
int p_new)
2289 crm_trace(
"Child element %s with id='%s' moved from position %d to %d under %s",
2290 new_child->name, (
ID(new_child)?
ID(new_child) :
"<no id>"),
2291 p_old, p_new, new_parent->name);
2292 mark_xml_node_dirty(new_parent);
2295 if (p_old > p_new) {
2296 p = old_child->_private;
2298 p = new_child->_private;
2305 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml,
bool check_top)
2307 xmlNode *cIter = NULL;
2311 if (old_xml == NULL) {
2317 p = new_xml->_private;
2326 xml_diff_attrs(old_xml, new_xml);
2329 for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2330 xmlNode *old_child = cIter;
2333 cIter = pcmk__xml_next(cIter);
2335 mark_xml_changes(old_child, new_child, TRUE);
2338 mark_child_deleted(old_child, new_xml);
2343 for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2344 xmlNode *new_child = cIter;
2347 cIter = pcmk__xml_next(cIter);
2348 if(old_child == NULL) {
2350 p = new_child->_private;
2352 mark_xml_changes(old_child, new_child, TRUE);
2359 if(p_old != p_new) {
2360 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2384 mark_xml_changes(old_xml, new_xml, FALSE);
2390 xmlNode *cIter = NULL;
2391 gboolean can_prune = TRUE;
2392 const char *name = crm_element_name(xml_node);
2399 for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2400 const char *p_name = (
const char *) a->name;
2408 cIter = pcmk__xml_first_child(xml_node);
2410 xmlNode *child = cIter;
2412 cIter = pcmk__xml_next(cIter);
2433 xmlNode *a_child = NULL;
2436 CRM_CHECK(search_comment->type == XML_COMMENT_NODE,
return NULL);
2438 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2439 a_child = pcmk__xml_next(a_child)) {
2444 if (offset < search_offset) {
2447 }
else if (offset > search_offset) {
2456 if (a_child->type == XML_COMMENT_NODE
2457 && pcmk__str_eq((
const char *)a_child->content, (
const char *)search_comment->content,
pcmk__str_casei)) {
2483 CRM_CHECK(update->type == XML_COMMENT_NODE,
return);
2485 if (target == NULL) {
2489 if (target == NULL) {
2492 }
else if (!pcmk__str_eq((
const char *)target->content, (
const char *)update->content,
pcmk__str_casei)) {
2493 xmlFree(target->content);
2494 target->content = xmlStrdup(update->content);
2514 xmlNode *a_child = NULL;
2515 const char *object_name = NULL,
2516 *object_href = NULL,
2517 *object_href_val = NULL;
2519 #if XML_PARSER_DEBUG
2526 if (update->type == XML_COMMENT_NODE) {
2531 object_name = crm_element_name(update);
2532 object_href_val =
ID(update);
2533 if (object_href_val != NULL) {
2541 CRM_CHECK(target != NULL || parent != NULL,
return);
2543 if (target == NULL) {
2545 object_href, object_href_val);
2548 if (target == NULL) {
2551 #if XML_PARSER_DEBUG
2553 object_href ?
" " :
"",
2554 object_href ? object_href :
"",
2555 object_href ?
"=" :
"",
2556 object_href ? object_href_val :
"");
2560 object_href ?
" " :
"",
2561 object_href ? object_href :
"",
2562 object_href ?
"=" :
"",
2563 object_href ? object_href_val :
"");
2567 CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2571 if (as_diff == FALSE) {
2577 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2579 const char *p_value = pcmk__xml_attr_value(a);
2582 xmlUnsetProp(target, a->name);
2583 xmlSetProp(target, a->name, (
pcmkXmlStr) p_value);
2587 for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2588 a_child = pcmk__xml_next(a_child)) {
2589 #if XML_PARSER_DEBUG
2591 object_href ?
" " :
"",
2592 object_href ? object_href :
"",
2593 object_href ?
"=" :
"",
2594 object_href ? object_href_val :
"");
2599 #if XML_PARSER_DEBUG
2601 object_href ?
" " :
"",
2602 object_href ? object_href :
"",
2603 object_href ?
"=" :
"",
2604 object_href ? object_href_val :
"");
2611 gboolean can_update = TRUE;
2612 xmlNode *child_of_child = NULL;
2615 CRM_CHECK(to_update != NULL,
return FALSE);
2617 if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child),
pcmk__str_casei)) {
2623 }
else if (can_update) {
2624 #if XML_PARSER_DEBUG
2630 for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2631 child_of_child = pcmk__xml_next(child_of_child)) {
2644 const char *tag,
const char *field,
const char *value, gboolean search_matches)
2646 int match_found = 0;
2649 CRM_CHECK(children != NULL,
return FALSE);
2651 if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root),
pcmk__str_casei)) {
2656 if (*children == NULL) {
2663 if (search_matches || match_found == 0) {
2664 xmlNode *child = NULL;
2666 for (child = pcmk__xml_first_child(root); child != NULL;
2667 child = pcmk__xml_next(child)) {
2668 match_found +=
find_xml_children(children, child, tag, field, value, search_matches);
2678 gboolean can_delete = FALSE;
2679 xmlNode *child_of_child = NULL;
2681 const char *up_id = NULL;
2682 const char *child_id = NULL;
2683 const char *right_val = NULL;
2686 CRM_CHECK(update != NULL,
return FALSE);
2689 child_id =
ID(child);
2691 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2694 if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child),
pcmk__str_casei)) {
2697 if (can_delete && delete_only) {
2698 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2700 const char *p_name = (
const char *) a->name;
2701 const char *p_value = pcmk__xml_attr_value(a);
2710 if (can_delete && parent != NULL) {
2712 if (delete_only || update == NULL) {
2717 xmlDoc *doc = tmp->doc;
2718 xmlNode *old = NULL;
2721 old = xmlReplaceNode(child, tmp);
2729 xmlDocSetRootElement(doc, old);
2735 }
else if (can_delete) {
2740 child_of_child = pcmk__xml_first_child(child);
2741 while (child_of_child) {
2742 xmlNode *next = pcmk__xml_next(child_of_child);
2748 child_of_child = NULL;
2750 child_of_child = next;
2760 xmlNode *child = NULL;
2761 GSList *nvpairs = NULL;
2762 xmlNode *result = NULL;
2763 const char *name = NULL;
2767 name = crm_element_name(input);
2776 for (child = pcmk__xml_first_child(input); child != NULL;
2777 child = pcmk__xml_next(child)) {
2792 xmlNode *match = NULL;
2794 for (match = pcmk__xe_first_child(parent); match != NULL;
2795 match = pcmk__xe_next(match)) {
2818 xmlNode *match = pcmk__xe_next(sibling);
2819 const char *name = crm_element_name(sibling);
2821 while (match != NULL) {
2822 if (!strcmp(crm_element_name(match), name)) {
2825 match = pcmk__xe_next(match);
2833 static bool init = TRUE;
2842 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2845 xmlDeregisterNodeDefault(free_private_data);
2846 xmlRegisterNodeDefault(new_private_data);
2855 crm_info(
"Cleaning up memory from libxml2");
2860 #define XPATH_MAX 512
2865 const char *tag = NULL;
2866 const char *ref = NULL;
2867 xmlNode *result = input;
2869 if (result == NULL) {
2872 }
else if (top == NULL) {
2876 tag = crm_element_name(result);
2883 if (result == NULL) {
2884 char *nodePath = (
char *)xmlGetNodePath(top);
2886 crm_err(
"No match for %s found in %s: Invalid configuration", xpath_string,
2904 static const char *base = NULL;
2908 base = getenv(
"PCMK_schema_directory");
2910 if (pcmk__str_empty(base)) {
2924 crm_err(
"XML artefact family specified as %u not recognized", ns);
2944 crm_err(
"XML artefact family specified as %u not recognized", ns);
2955 const char *
name, *value;
2957 name = va_arg(pairs,
const char *);
2962 value = va_arg(pairs,
const char *);
2963 if (value == NULL) {
2975 va_start(pairs, node);
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
#define CRM_CHECK(expr, failure_action)
xmlNode * find_xml_node(xmlNode *cib, const char *node_path, gboolean must_find)
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
void crm_schema_init(void)
#define PCMK__XML_PARSE_OPTS
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 pcmk__free_acls(GList *acls)
int pcmk_rc2legacy(int rc)
G_GNUC_INTERNAL void pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
const char * pcmk__xe_add_last_written(xmlNode *xe)
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.
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
int char2score(const char *score)
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
#define buffer_print(buffer, max, offset, fmt, args...)
xmlNode * pcmk__xe_match(xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
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.
void pcmk__xe_set_props(xmlNodePtr node,...)
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
#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.
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.
G_GNUC_INTERNAL xmlNode * pcmk__xc_match(xmlNode *root, xmlNode *search_comment, bool exact)
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Deprecated Pacemaker XML API.
xmlNode * string2xml(const char *input)
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
xmlDoc * getDocPtr(xmlNode *node)
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
bool xml_tracking_changes(xmlNode *xml)
G_GNUC_INTERNAL void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
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...)
void pcmk__strip_xml_text(xmlNode *xml)
G_GNUC_INTERNAL void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
xmlNode * stdin2xml(void)
void pcmk_free_xml_subtree(xmlNode *xml)
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.
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
#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.
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
#define crm_log_xml_debug(xml, text)
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
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)
#define XML_TAG_RESOURCE_REF
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)
void free_xml(xmlNode *child)
gboolean xml_has_children(const xmlNode *root)
const char * pcmk__epoch2str(time_t *when)
G_GNUC_INTERNAL xmlNode * pcmk__xml_match(xmlNode *haystack, xmlNode *needle, bool exact)
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
const xmlChar * pcmkXmlStr
G_GNUC_INTERNAL void pcmk__xe_log(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, int options)
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
void xml_log_changes(uint8_t level, const char *function, xmlNode *xml)
G_GNUC_INTERNAL void pcmk__xml2text(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
#define PCMK__BUFFER_SIZE
#define crm_log_xml_err(xml, text)
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
#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__apply_acl(xmlNode *xml)
void xml_remove_prop(xmlNode *obj, const char *name)
gboolean can_prune_leaf(xmlNode *xml_node)
char * dump_xml_unformatted(xmlNode *msg)
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)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
#define XML_CIB_TAG_OBJ_REF
#define crm_log_xml_trace(xml, text)
#define XML_ACL_TAG_ROLE_REF
#define attr_matches(c, n, v)
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
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.
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
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)
G_GNUC_INTERNAL void pcmk__mark_xml_created(xmlNode *xml)
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
void fix_plus_plus_recursive(xmlNode *target)
G_GNUC_INTERNAL void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
#define crm_info(fmt, args...)
G_GNUC_INTERNAL int pcmk__xml_position(xmlNode *xml, enum xml_private_flags ignore_if_set)
char * dump_xml_formatted_with_text(xmlNode *msg)
bool pcmk__ends_with_ext(const char *s, const char *match)
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.