18 #include <sys/types.h> 21 #include <libxml/parser.h> 22 #include <libxml/tree.h> 23 #include <libxml/xmlstring.h> 31 #define XML_VERSION ((pcmkXmlStr) "1.0") 54 if (!fn(xml, user_data)) {
58 for (xml = pcmk__xml_first_child(xml); xml != NULL;
59 xml = pcmk__xml_next(xml)) {
71 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
86 for (; xml != NULL; xml = xml->parent) {
89 if (nodepriv != NULL) {
129 if (nodepriv != NULL) {
147 mark_xml_dirty_created(xmlNode *xml,
void *user_data)
151 if (nodepriv != NULL) {
166 mark_xml_tree_dirty_created(xmlNode *xml)
183 free_deleted_object(
void *
data)
188 g_free(deleted_obj->
path);
197 if (docpriv != NULL) {
201 docpriv->
user = NULL;
203 if (docpriv->
acls != NULL) {
205 docpriv->
acls = NULL;
209 g_list_free_full(docpriv->
deleted_objs, free_deleted_object);
227 new_private_data(xmlNode *node,
void *user_data)
231 if (node->_private != NULL) {
235 switch (node->type) {
236 case XML_DOCUMENT_NODE:
242 node->_private = docpriv;
247 case XML_ELEMENT_NODE:
248 case XML_ATTRIBUTE_NODE:
249 case XML_COMMENT_NODE:
255 node->_private = nodepriv;
258 for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
261 new_private_data((xmlNode *) iter, user_data);
268 case XML_CDATA_SECTION_NODE:
294 free_private_data(xmlNode *node,
void *user_data)
298 if (node->_private == NULL) {
302 if (node->type == XML_DOCUMENT_NODE) {
310 for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
313 free_private_data((xmlNode *) iter, user_data);
316 free(node->_private);
317 node->_private = NULL;
349 crm_trace(
"Tracking changes%s to %p", enforce_acls?
" with ACLs":
"", xml);
352 if(acl_source == NULL) {
363 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
370 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
389 for (
const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
412 accept_attr_deletions(xmlNode *xml,
void *user_data)
432 if (needle->type == XML_COMMENT_NODE) {
436 const char *
id = pcmk__xe_id(needle);
437 const char *attr = (
id == NULL)? NULL :
PCMK_XA_ID;
454 crm_trace(
"Accepting changes to %p", xml);
455 docpriv = xml->doc->_private;
456 top = xmlDocGetRootElement(xml->doc);
458 reset_xml_private_data(xml->doc->_private);
545 GString *buf = g_string_sized_new(32);
547 for (
int i = 0; (i < 4) && (utf8[i] !=
'\0'); i++) {
548 g_string_append_printf(buf,
" 0x%.2X", utf8[i]);
550 crm_info(
"Invalid UTF-8 character (bytes:%s)",
551 (pcmk__str_empty(buf->str)?
" <none>" : buf->str));
552 g_string_free(buf, TRUE);
558 || ((c >=
'a') && (c <=
'z'))
559 || ((c >=
'A') && (c <=
'Z'))
560 || ((c >= 0xC0) && (c <= 0xD6))
561 || ((c >= 0xD8) && (c <= 0xF6))
562 || ((c >= 0xF8) && (c <= 0x2FF))
563 || ((c >= 0x370) && (c <= 0x37D))
564 || ((c >= 0x37F) && (c <= 0x1FFF))
565 || ((c >= 0x200C) && (c <= 0x200D))
566 || ((c >= 0x2070) && (c <= 0x218F))
567 || ((c >= 0x2C00) && (c <= 0x2FEF))
568 || ((c >= 0x3001) && (c <= 0xD7FF))
569 || ((c >= 0xF900) && (c <= 0xFDCF))
570 || ((c >= 0xFDF0) && (c <= 0xFFFD))
571 || ((c >= 0x10000) && (c <= 0xEFFFF));
606 GString *buf = g_string_sized_new(32);
608 for (
int i = 0; (i < 4) && (utf8[i] !=
'\0'); i++) {
609 g_string_append_printf(buf,
" 0x%.2X", utf8[i]);
611 crm_info(
"Invalid UTF-8 character (bytes:%s)",
612 (pcmk__str_empty(buf->str)?
" <none>" : buf->str));
613 g_string_free(buf, TRUE);
617 return ((c >=
'a') && (c <=
'z'))
618 || ((c >=
'A') && (c <=
'Z'))
619 || ((c >=
'0') && (c <=
'9'))
625 || ((c >= 0xC0) && (c <= 0xD6))
626 || ((c >= 0xD8) && (c <= 0xF6))
627 || ((c >= 0xF8) && (c <= 0x2FF))
628 || ((c >= 0x300) && (c <= 0x36F))
629 || ((c >= 0x370) && (c <= 0x37D))
630 || ((c >= 0x37F) && (c <= 0x1FFF))
631 || ((c >= 0x200C) && (c <= 0x200D))
632 || ((c >= 0x203F) && (c <= 0x2040))
633 || ((c >= 0x2070) && (c <= 0x218F))
634 || ((c >= 0x2C00) && (c <= 0x2FEF))
635 || ((c >= 0x3001) && (c <= 0xD7FF))
636 || ((c >= 0xF900) && (c <= 0xFDCF))
637 || ((c >= 0xFDF0) && (c <= 0xFFFD))
638 || ((c >= 0x10000) && (c <= 0xEFFFF));
673 for (
int i = 1; i < len; i++) {
678 for (
id += len; *
id !=
'\0';
id += len) {
682 for (
int i = 0; i < len; i++) {
715 free_xml_with_position(xmlNode *node,
int position)
724 nodepriv = node->_private;
726 if ((doc != NULL) && (xmlDocGetRootElement(doc) == node)) {
735 GString *xpath = NULL;
739 qb_log_from_external_source(__func__, __FILE__,
741 __LINE__, 0, xpath->str, nodepriv->
flags);
742 g_string_free(xpath, TRUE);
755 crm_trace(
"Deleting %s %p from %p", xpath->str, node, doc);
758 deleted_obj->
path = g_string_free(xpath, FALSE);
762 if (node->type == XML_COMMENT_NODE) {
791 free_xml_with_position(xml, -1);
807 xmlNode *copy = NULL;
820 copy = xmlDocCopyNode(src, doc, 1);
823 xmlDocSetRootElement(doc, copy);
826 copy = xmlDocCopyNode(src,
parent->doc, 1);
829 xmlAddChild(
parent, copy);
845 xmlNode *iter = xml->children;
848 xmlNode *next = iter->next;
850 switch (iter->type) {
855 case XML_ELEMENT_NODE:
888 while (*text !=
'\0') {
900 if (g_ascii_iscntrl(*text)) {
915 if (g_ascii_iscntrl(*text)) {
939 text = g_utf8_next_char(text);
966 GString *copy = NULL;
971 copy = g_string_sized_new(strlen(text));
973 while (*text !=
'\0') {
975 if ((*text & 0x80) != 0) {
976 size_t bytes = g_utf8_next_char(text) - text;
978 g_string_append_len(copy, text, bytes);
997 g_string_append_c(copy, *text);
1000 if (g_ascii_iscntrl(*text)) {
1001 g_string_append_printf(copy,
"&#x%.2X;", *text);
1003 g_string_append_c(copy, *text);
1024 if (g_ascii_iscntrl(*text)) {
1025 g_string_append_printf(copy,
"&#x%.2X;", *text);
1027 g_string_append_c(copy, *text);
1036 g_string_append(copy,
"\\\"");
1039 g_string_append(copy,
"\\n");
1042 g_string_append(copy,
"\\r");
1045 g_string_append(copy,
"\\t");
1048 g_string_append_c(copy, *text);
1058 text = g_utf8_next_char(text);
1060 return g_string_free(copy, FALSE);
1073 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1093 mark_attr_deleted(xmlNode *new_xml,
const char *element,
const char *attr_name,
1094 const char *old_value)
1097 xmlAttr *attr = NULL;
1108 attr = xmlHasProp(new_xml, (
pcmkXmlStr) attr_name);
1109 nodepriv = attr->_private;
1110 nodepriv->
flags = 0;
1115 crm_trace(
"XML attribute %s=%s was removed from %s",
1116 attr_name, old_value, element);
1124 mark_attr_changed(xmlNode *new_xml,
const char *element,
const char *attr_name,
1125 const char *old_value)
1130 crm_trace(
"XML attribute %s was changed from '%s' to '%s' in %s",
1131 attr_name, old_value, vcopy, element);
1155 mark_attr_moved(xmlNode *new_xml,
const char *element, xmlAttr *old_attr,
1156 xmlAttr *new_attr,
int p_old,
int p_new)
1160 crm_trace(
"XML attribute %s moved from position %d to %d in %s",
1161 old_attr->name, p_old, p_new, element);
1169 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1181 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1183 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1185 while (attr_iter != NULL) {
1186 const char *
name = (
const char *) attr_iter->name;
1187 xmlAttr *old_attr = attr_iter;
1188 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1189 const char *old_value = pcmk__xml_attr_value(attr_iter);
1191 attr_iter = attr_iter->next;
1192 if (new_attr == NULL) {
1193 mark_attr_deleted(new_xml, (
const char *) old_xml->name,
name,
1207 if (strcmp(new_value, old_value) != 0) {
1208 mark_attr_changed(new_xml, (
const char *) old_xml->name,
name,
1211 }
else if ((old_pos != new_pos)
1213 mark_attr_moved(new_xml, (
const char *) old_xml->name,
1214 old_attr, new_attr, old_pos, new_pos);
1230 mark_created_attrs(xmlNode *new_xml)
1232 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1234 while (attr_iter != NULL) {
1235 xmlAttr *new_attr = attr_iter;
1238 attr_iter = attr_iter->next;
1240 const char *attr_name = (
const char *) new_attr->name;
1242 crm_trace(
"Created new attribute %s=%s in %s",
1243 attr_name, pcmk__xml_attr_value(new_attr),
1267 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1270 xml_diff_old_attrs(old_xml, new_xml);
1271 mark_created_attrs(new_xml);
1287 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1299 free_xml_with_position(candidate,
1309 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
1310 int p_old,
int p_new)
1315 PCMK_XA_ID "='%s' moved from position %d to %d under %s",
1316 new_child->name, pcmk__s(pcmk__xe_id(new_child),
"<no id>"),
1317 p_old, p_new, new_parent->name);
1321 if (p_old > p_new) {
1322 nodepriv = old_child->_private;
1324 nodepriv = new_child->_private;
1331 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml,
bool check_top)
1333 xmlNode *old_child = NULL;
1334 xmlNode *new_child = NULL;
1338 if (old_xml == NULL) {
1339 mark_xml_tree_dirty_created(new_xml);
1344 nodepriv = new_xml->_private;
1353 xml_diff_attrs(old_xml, new_xml);
1356 for (old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1357 old_child = pcmk__xml_next(old_child)) {
1361 if (new_child != NULL) {
1362 mark_xml_changes(old_child, new_child,
true);
1365 mark_child_deleted(old_child, new_xml);
1370 new_child = pcmk__xml_first_child(new_xml);
1371 while (new_child != NULL) {
1372 xmlNode *next = pcmk__xml_next(new_child);
1376 if (old_child == NULL) {
1378 nodepriv = new_child->_private;
1382 mark_xml_changes(old_child, new_child,
true);
1389 if(p_old != p_new) {
1390 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
1409 CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1410 && pcmk__xe_is(old_xml, (
const char *) new_xml->name)
1411 && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
1419 mark_xml_changes(old_xml, new_xml, FALSE);
1433 static bool initialized =
false;
1442 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
1465 static const char *base = NULL;
1471 if (pcmk__str_empty(base)) {
1485 crm_err(
"XML artefact family specified as %u not recognized", ns);
1513 crm_err(
"XML artefact family specified as %u not recognized", ns);
1526 ret = find_artefact(ns, base, filespec);
1529 if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) {
1533 ret = find_artefact(ns, remote_schema_dir, filespec);
1548 xmlNode *copy = NULL;
1550 copy = xmlDocCopyNode(src, doc, 1);
1553 xmlDocSetRootElement(doc, copy);
1587 for (c =
id; *c; ++c) {
#define CRM_CHECK(expr, failure_action)
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
#define XML_VERSION
libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5
void pcmk__xml_free_private_data(xmlNode *xml)
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
#define PCMK__XML_ENTITY_LT
#define pcmk__if_tracing(if_action, else_action)
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
void pcmk__free_acls(GList *acls)
enum pcmk_ipc_server type
void pcmk__xml_sanitize_id(char *id)
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
xmlDoc * pcmk__xml_new_doc(void)
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
#define PCMK__XML_DOC_PRIVATE_MAGIC
void crm_xml_sanitize_id(char *id)
void pcmk__xml_new_private_data(xmlNode *xml)
void pcmk_free_xml_subtree(xmlNode *xml)
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
#define CRM_LOG_ASSERT(expr)
bool pcmk__xml_is_name_start_char(const char *utf8, int *len)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
bool pcmk__ends_with(const char *s, const char *match)
const char * pcmk__env_option(const char *option)
void pcmk__xml_free(xmlNode *xml)
Deprecated Pacemaker XML API.
G_GNUC_INTERNAL bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
void pcmk__schema_init(void)
gchar * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
#define PCMK__ENV_SCHEMA_DIRECTORY
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
void pcmk__strip_xml_text(xmlNode *xml)
xmlNode * copy_xml(xmlNode *src)
void pcmk__xml_free_doc(xmlDoc *doc)
#define PCMK__XML_ENTITY_GT
void pcmk__xml_cleanup(void)
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.
void free_xml(xmlNode *child)
#define crm_trace(fmt, args...)
void pcmk__mark_xml_node_dirty(xmlNode *xml)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
void pcmk__xml_set_parent_flags(xmlNode *xml, uint64_t flags)
Wrappers for and extensions to libxml2.
void pcmk__schema_cleanup(void)
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
void crm_xml_cleanup(void)
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
void pcmk__xml_init(void)
const xmlChar * pcmkXmlStr
#define PCMK__XML_NODE_PRIVATE_MAGIC
#define pcmk__assert(expr)
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
#define crm_err(fmt, args...)
const char * pcmk__remote_schema_dir(void)
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
void pcmk__apply_acl(xmlNode *xml)
void xml_accept_changes(xmlNode *xml)
#define pcmk__mem_assert(ptr)
#define PCMK__XML_ENTITY_QUOT
bool pcmk__xml_reset_node_flags(xmlNode *xml, void *user_data)
bool pcmk__xml_is_name_char(const char *utf8, int *len)
GString * pcmk__element_xpath(const xmlNode *xml)
bool xml_tracking_changes(xmlNode *xml)
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
G_GNUC_INTERNAL xmlNode * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
#define PCMK__XML_ENTITY_AMP
#define pcmk__assert_alloc(nmemb, size)
#define crm_info(fmt, args...)
G_GNUC_INTERNAL int pcmk__xa_remove(xmlAttr *attr, bool force)
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
void pcmk__xml_free_node(xmlNode *xml)
bool xml_document_dirty(xmlNode *xml)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1