diff options
Diffstat (limited to 'lib/common/xml.c')
-rw-r--r-- | lib/common/xml.c | 527 |
1 files changed, 255 insertions, 272 deletions
diff --git a/lib/common/xml.c b/lib/common/xml.c index 22078ce..53ebff7 100644 --- a/lib/common/xml.c +++ b/lib/common/xml.c @@ -42,7 +42,8 @@ * parsing without XML_PARSE_RECOVER, and if that fails, try parsing again with * it, logging a warning if it succeeds. */ -#define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER) +#define PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER (XML_PARSE_NOBLANKS) +#define PCMK__XML_PARSE_OPTS_WITH_RECOVER (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER) bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy) @@ -85,8 +86,8 @@ pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag) } // Mark document, element, and all element's parents as changed -static inline void -mark_xml_node_dirty(xmlNode *xml) +void +pcmk__mark_xml_node_dirty(xmlNode *xml) { pcmk__set_xml_doc_flag(xml, pcmk__xf_dirty); set_parent_flag(xml, pcmk__xf_dirty); @@ -114,12 +115,15 @@ void pcmk__mark_xml_created(xmlNode *xml) { xmlNode *cIter = NULL; - xml_node_private_t *nodepriv = xml->_private; + xml_node_private_t *nodepriv = NULL; + + CRM_ASSERT(xml != NULL); + nodepriv = xml->_private; if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) { if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) { pcmk__set_xml_flags(nodepriv, pcmk__xf_created); - mark_xml_node_dirty(xml); + pcmk__mark_xml_node_dirty(xml); } for (cIter = pcmk__xml_first_child(xml); cIter != NULL; cIter = pcmk__xml_next(cIter)) { @@ -128,17 +132,6 @@ pcmk__mark_xml_created(xmlNode *xml) } } -void -pcmk__mark_xml_attr_dirty(xmlAttr *a) -{ - xmlNode *parent = a->parent; - xml_node_private_t *nodepriv = a->_private; - - pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_modified); - pcmk__clear_xml_flags(nodepriv, pcmk__xf_deleted); - mark_xml_node_dirty(parent); -} - #define XML_DOC_PRIVATE_MAGIC 0x81726354UL #define XML_NODE_PRIVATE_MAGIC 0x54637281UL @@ -250,7 +243,7 @@ new_private_data(xmlNode *node) /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is * not hooked up at the point we are called */ - mark_xml_node_dirty(node); + pcmk__mark_xml_node_dirty(node); } break; } @@ -321,19 +314,6 @@ pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set) return position; } -// This also clears attribute's flags if not marked as deleted -static bool -marked_as_deleted(xmlAttrPtr a, void *user_data) -{ - xml_node_private_t *nodepriv = a->_private; - - if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) { - return true; - } - nodepriv->flags = pcmk__xf_none; - return false; -} - // Remove all attributes marked as deleted from an XML node static void accept_attr_deletions(xmlNode *xml) @@ -342,7 +322,7 @@ accept_attr_deletions(xmlNode *xml) ((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none; // Remove this XML node's attributes that were marked as deleted - pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL); + pcmk__xe_remove_matching_attrs(xml, pcmk__marked_as_deleted, NULL); // Recursively do the same for this XML node's children for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL; @@ -371,7 +351,7 @@ pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact) const char *id = ID(needle); const char *attr = (id == NULL)? NULL : XML_ATTR_ID; - return pcmk__xe_match(haystack, crm_element_name(needle), attr, id); + return pcmk__xe_match(haystack, (const char *) needle->name, attr, id); } } @@ -404,11 +384,7 @@ xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find) { xmlNode *a_child = NULL; - const char *name = "NULL"; - - if (root != NULL) { - name = crm_element_name(root); - } + const char *name = (root == NULL)? "<NULL>" : (const char *) root->name; if (search_path == NULL) { crm_warn("Will never find <NULL>"); @@ -418,7 +394,6 @@ find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find) for (a_child = pcmk__xml_first_child(root); a_child != NULL; a_child = pcmk__xml_next(a_child)) { if (strcmp((const char *)a_child->name, search_path) == 0) { -/* crm_trace("returning node (%s).", crm_element_name(a_child)); */ return a_child; } } @@ -473,7 +448,7 @@ pcmk__xe_match(const xmlNode *parent, const char *node_name, (attr_n? attr_n : ""), (attr_n? "=" : ""), (attr_n? attr_v : ""), - crm_element_name(parent)); + (const char *) parent->name); return NULL; } @@ -643,31 +618,17 @@ pcmk__xe_remove_matching_attrs(xmlNode *element, } } -xmlDoc * -getDocPtr(xmlNode * node) -{ - xmlDoc *doc = NULL; - - CRM_CHECK(node != NULL, return NULL); - - doc = node->doc; - if (doc == NULL) { - doc = xmlNewDoc((pcmkXmlStr) "1.0"); - xmlDocSetRootElement(doc, node); - xmlSetTreeDoc(node, doc); - } - return doc; -} - xmlNode * add_node_copy(xmlNode * parent, xmlNode * src_node) { xmlNode *child = NULL; - xmlDoc *doc = getDocPtr(parent); - CRM_CHECK(src_node != NULL, return NULL); + CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL); - child = xmlDocCopyNode(src_node, doc, 1); + child = xmlDocCopyNode(src_node, parent->doc, 1); + if (child == NULL) { + return NULL; + } xmlAddChild(parent, child); pcmk__mark_xml_created(child); return child; @@ -686,13 +647,22 @@ create_xml_node(xmlNode * parent, const char *name) if (parent == NULL) { doc = xmlNewDoc((pcmkXmlStr) "1.0"); + if (doc == NULL) { + return NULL; + } + node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL); + if (node == NULL) { + xmlFreeDoc(doc); + return NULL; + } xmlDocSetRootElement(doc, node); } else { - doc = getDocPtr(parent); - node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL); - xmlAddChild(parent, node); + node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL); + if (node == NULL) { + return NULL; + } } pcmk__mark_xml_created(node); return node; @@ -823,7 +793,6 @@ copy_xml(xmlNode * src) CRM_ASSERT(copy != NULL); xmlDocSetRootElement(doc, copy); - xmlSetTreeDoc(copy, doc); return copy; } @@ -833,7 +802,7 @@ string2xml(const char *input) xmlNode *xml = NULL; xmlDocPtr output = NULL; xmlParserCtxtPtr ctxt = NULL; - xmlErrorPtr last_error = NULL; + const xmlError *last_error = NULL; if (input == NULL) { crm_err("Can't parse NULL input"); @@ -847,7 +816,17 @@ string2xml(const char *input) xmlCtxtResetLastError(ctxt); xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err); output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, - PCMK__XML_PARSE_OPTS); + PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); + + if (output == NULL) { + output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, + PCMK__XML_PARSE_OPTS_WITH_RECOVER); + if (output) { + crm_warn("Successfully recovered from XML errors " + "(note: a future release will treat this as a fatal failure)"); + } + } + if (output) { xml = xmlDocGetRootElement(output); } @@ -933,9 +912,11 @@ decompress_file(const char *filename) } bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0); - if (rc != BZ_OK) { + rc = pcmk__bzlib2rc(rc); + + if (rc != pcmk_rc_ok) { crm_err("Could not prepare to read compressed %s: %s " - CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc); + CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc); BZ2_bzReadClose(&rc, bz_file); fclose(input); return NULL; @@ -957,9 +938,11 @@ decompress_file(const char *filename) buffer[length] = '\0'; - if (rc != BZ_STREAM_END) { - crm_err("Could not read compressed %s: %s " - CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc); + rc = pcmk__bzlib2rc(rc); + + if (rc != pcmk_rc_ok) { + crm_err("Could not read compressed %s: %s " CRM_XS " rc=%d", + filename, pcmk_rc_str(rc), rc); free(buffer); buffer = NULL; } @@ -1010,7 +993,7 @@ filename2xml(const char *filename) xmlDocPtr output = NULL; bool uncompressed = true; xmlParserCtxtPtr ctxt = NULL; - xmlErrorPtr last_error = NULL; + const xmlError *last_error = NULL; /* create a parser context */ ctxt = xmlNewParserCtxt(); @@ -1026,16 +1009,45 @@ filename2xml(const char *filename) if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) { /* STDIN_FILENO == fileno(stdin) */ output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, - PCMK__XML_PARSE_OPTS); + PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); + + if (output == NULL) { + output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, + PCMK__XML_PARSE_OPTS_WITH_RECOVER); + if (output) { + crm_warn("Successfully recovered from XML errors " + "(note: a future release will treat this as a fatal failure)"); + } + } } else if (uncompressed) { - output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS); + output = xmlCtxtReadFile(ctxt, filename, NULL, + PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); + + if (output == NULL) { + output = xmlCtxtReadFile(ctxt, filename, NULL, + PCMK__XML_PARSE_OPTS_WITH_RECOVER); + if (output) { + crm_warn("Successfully recovered from XML errors " + "(note: a future release will treat this as a fatal failure)"); + } + } } else { char *input = decompress_file(filename); output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, - PCMK__XML_PARSE_OPTS); + PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); + + if (output == NULL) { + output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, + PCMK__XML_PARSE_OPTS_WITH_RECOVER); + if (output) { + crm_warn("Successfully recovered from XML errors " + "(note: a future release will treat this as a fatal failure)"); + } + } + free(input); } @@ -1134,7 +1146,7 @@ crm_xml_set_id(xmlNode *xml, const char *format, ...) * \internal * \brief Write XML to a file stream * - * \param[in] xml_node XML to write + * \param[in] xml XML to write * \param[in] filename Name of file being written (for logging only) * \param[in,out] stream Open file stream corresponding to filename * \param[in] compress Whether to compress XML before writing @@ -1143,18 +1155,18 @@ crm_xml_set_id(xmlNode *xml, const char *format, ...) * \return Standard Pacemaker return code */ static int -write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream, +write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream, bool compress, unsigned int *nbytes) { int rc = pcmk_rc_ok; char *buffer = NULL; *nbytes = 0; - crm_log_xml_trace(xml_node, "writing"); + crm_log_xml_trace(xml, "writing"); - buffer = dump_xml_formatted(xml_node); + buffer = dump_xml_formatted(xml); CRM_CHECK(buffer && strlen(buffer), - crm_log_xml_warn(xml_node, "formatting failed"); + crm_log_xml_warn(xml, "formatting failed"); rc = pcmk_rc_error; goto bail); @@ -1164,24 +1176,30 @@ write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream, rc = BZ_OK; bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30); - if (rc != BZ_OK) { + rc = pcmk__bzlib2rc(rc); + + if (rc != pcmk_rc_ok) { crm_warn("Not compressing %s: could not prepare file stream: %s " - CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc); + CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc); } else { BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer)); - if (rc != BZ_OK) { + rc = pcmk__bzlib2rc(rc); + + if (rc != pcmk_rc_ok) { crm_warn("Not compressing %s: could not compress data: %s " - CRM_XS " bzerror=%d errno=%d", - filename, bz2_strerror(rc), rc, errno); + CRM_XS " rc=%d errno=%d", + filename, pcmk_rc_str(rc), rc, errno); } } - if (rc == BZ_OK) { + if (rc == pcmk_rc_ok) { BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes); - if (rc != BZ_OK) { + rc = pcmk__bzlib2rc(rc); + + if (rc != pcmk_rc_ok) { crm_warn("Not compressing %s: could not write compressed data: %s " - CRM_XS " bzerror=%d errno=%d", - filename, bz2_strerror(rc), rc, errno); + CRM_XS " rc=%d errno=%d", + filename, pcmk_rc_str(rc), rc, errno); *nbytes = 0; // retry without compression } else { crm_trace("Compressed XML for %s from %u bytes to %u", @@ -1226,7 +1244,7 @@ write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream, /*! * \brief Write XML to a file descriptor * - * \param[in] xml_node XML to write + * \param[in] xml XML to write * \param[in] filename Name of file being written (for logging only) * \param[in] fd Open file descriptor corresponding to filename * \param[in] compress Whether to compress XML before writing @@ -1234,18 +1252,19 @@ write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream, * \return Number of bytes written on success, -errno otherwise */ int -write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress) +write_xml_fd(const xmlNode *xml, const char *filename, int fd, + gboolean compress) { FILE *stream = NULL; unsigned int nbytes = 0; int rc = pcmk_rc_ok; - CRM_CHECK(xml_node && (fd > 0), return -EINVAL); + CRM_CHECK((xml != NULL) && (fd > 0), return -EINVAL); stream = fdopen(fd, "w"); if (stream == NULL) { return -errno; } - rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes); + rc = write_xml_stream(xml, filename, stream, compress, &nbytes); if (rc != pcmk_rc_ok) { return pcmk_rc2legacy(rc); } @@ -1255,25 +1274,25 @@ write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress /*! * \brief Write XML to a file * - * \param[in] xml_node XML to write + * \param[in] xml XML to write * \param[in] filename Name of file to write * \param[in] compress Whether to compress XML before writing * * \return Number of bytes written on success, -errno otherwise */ int -write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress) +write_xml_file(const xmlNode *xml, const char *filename, gboolean compress) { FILE *stream = NULL; unsigned int nbytes = 0; int rc = pcmk_rc_ok; - CRM_CHECK(xml_node && filename, return -EINVAL); + CRM_CHECK((xml != NULL) && (filename != NULL), return -EINVAL); stream = fopen(filename, "w"); if (stream == NULL) { return -errno; } - rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes); + rc = write_xml_stream(xml, filename, stream, compress, &nbytes); if (rc != pcmk_rc_ok) { return pcmk_rc2legacy(rc); } @@ -1382,37 +1401,6 @@ crm_xml_escape(const char *text) /*! * \internal - * \brief Append an XML attribute to a buffer - * - * \param[in] attr Attribute to append - * \param[in,out] buffer Where to append the content (must not be \p NULL) - */ -static void -dump_xml_attr(const xmlAttr *attr, GString *buffer) -{ - char *p_value = NULL; - const char *p_name = NULL; - xml_node_private_t *nodepriv = NULL; - - if (attr == NULL || attr->children == NULL) { - return; - } - - nodepriv = attr->_private; - if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) { - return; - } - - p_name = (const char *) attr->name; - p_value = crm_xml_escape((const char *)attr->children->content); - pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"", - NULL); - - free(p_value); -} - -/*! - * \internal * \brief Append a string representation of an XML element to a buffer * * \param[in] data XML whose representation to append @@ -1424,24 +1412,21 @@ static void dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer, int depth) { - const char *name = crm_element_name(data); bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered); int spaces = pretty? (2 * depth) : 0; - CRM_ASSERT(name != NULL); - for (int lpc = 0; lpc < spaces; lpc++) { g_string_append_c(buffer, ' '); } - pcmk__g_strcat(buffer, "<", name, NULL); + pcmk__g_strcat(buffer, "<", data->name, NULL); for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL; attr = attr->next) { if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) { - dump_xml_attr(attr, buffer); + pcmk__dump_xml_attr(attr, buffer); } } @@ -1457,16 +1442,16 @@ dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer, } if (data->children) { - xmlNode *xChild = NULL; - for(xChild = data->children; xChild != NULL; xChild = xChild->next) { - pcmk__xml2text(xChild, options, buffer, depth + 1); + for (const xmlNode *child = data->children; child != NULL; + child = child->next) { + pcmk__xml2text(child, options, buffer, depth + 1); } for (int lpc = 0; lpc < spaces; lpc++) { g_string_append_c(buffer, ' '); } - pcmk__g_strcat(buffer, "</", name, ">", NULL); + pcmk__g_strcat(buffer, "</", data->name, ">", NULL); if (pretty) { g_string_append_c(buffer, '\n'); @@ -1559,7 +1544,45 @@ dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer, } } -#define PCMK__XMLDUMP_STATS 0 +/*! + * \internal + * \brief Get a string representation of an XML element type + * + * \param[in] type XML element type + * + * \return String representation of \p type + */ +static const char * +xml_element_type2str(xmlElementType type) +{ + static const char *const element_type_names[] = { + [XML_ELEMENT_NODE] = "element", + [XML_ATTRIBUTE_NODE] = "attribute", + [XML_TEXT_NODE] = "text", + [XML_CDATA_SECTION_NODE] = "CDATA section", + [XML_ENTITY_REF_NODE] = "entity reference", + [XML_ENTITY_NODE] = "entity", + [XML_PI_NODE] = "PI", + [XML_COMMENT_NODE] = "comment", + [XML_DOCUMENT_NODE] = "document", + [XML_DOCUMENT_TYPE_NODE] = "document type", + [XML_DOCUMENT_FRAG_NODE] = "document fragment", + [XML_NOTATION_NODE] = "notation", + [XML_HTML_DOCUMENT_NODE] = "HTML document", + [XML_DTD_NODE] = "DTD", + [XML_ELEMENT_DECL] = "element declaration", + [XML_ATTRIBUTE_DECL] = "attribute declaration", + [XML_ENTITY_DECL] = "entity declaration", + [XML_NAMESPACE_DECL] = "namespace declaration", + [XML_XINCLUDE_START] = "XInclude start", + [XML_XINCLUDE_END] = "XInclude end", + }; + + if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) { + return "unrecognized type"; + } + return element_type_names[type]; +} /*! * \internal @@ -1571,7 +1594,8 @@ dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer, * \param[in] depth Current indentation level */ void -pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth) +pcmk__xml2text(const xmlNode *data, uint32_t options, GString *buffer, + int depth) { if (data == NULL) { crm_trace("Nothing to dump"); @@ -1581,60 +1605,6 @@ pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth) CRM_ASSERT(buffer != NULL); CRM_CHECK(depth >= 0, depth = 0); - if (pcmk_is_set(options, pcmk__xml_fmt_full)) { - /* libxml's serialization reuse is a good idea, sadly we cannot - apply it for the filtered cases (preceding filtering pass - would preclude further reuse of such in-situ modified XML - in generic context and is likely not a win performance-wise), - and there's also a historically unstable throughput argument - (likely stemming from memory allocation overhead, eventhough - that shall be minimized with defaults preset in crm_xml_init) */ -#if (PCMK__XMLDUMP_STATS - 0) - time_t next, new = time(NULL); -#endif - xmlDoc *doc; - xmlOutputBuffer *xml_buffer; - - doc = getDocPtr(data); - /* doc will only be NULL if data is */ - CRM_CHECK(doc != NULL, return); - - xml_buffer = xmlAllocOutputBuffer(NULL); - CRM_ASSERT(xml_buffer != NULL); - - /* XXX we could setup custom allocation scheme for the particular - buffer, but it's subsumed with crm_xml_init that needs to - be invoked prior to entering this function as such, since - its other branch vitally depends on it -- what can be done - about this all is to have a facade parsing functions that - would 100% mark entering libxml code for us, since we don't - do anything as crazy as swapping out the binary form of the - parsed tree (but those would need to be strictly used as - opposed to libxml's raw functions) */ - - xmlNodeDumpOutput(xml_buffer, doc, data, 0, - pcmk_is_set(options, pcmk__xml_fmt_pretty), NULL); - /* attempt adding final NL - failing shouldn't be fatal here */ - (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n"); - if (xml_buffer->buffer != NULL) { - g_string_append(buffer, - (const gchar *) xmlBufContent(xml_buffer->buffer)); - } - -#if (PCMK__XMLDUMP_STATS - 0) - next = time(NULL); - if ((now + 1) < next) { - crm_log_xml_trace(data, "Long time"); - crm_err("xmlNodeDumpOutput() -> %lld bytes took %ds", - (long long) buffer->len, next - now); - } -#endif - - /* asserted allocation before so there should be something to remove */ - (void) xmlOutputBufferClose(xml_buffer); - return; - } - switch(data->type) { case XML_ELEMENT_NODE: /* Handle below */ @@ -1642,11 +1612,6 @@ pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth) break; case XML_TEXT_NODE: if (pcmk_is_set(options, pcmk__xml_fmt_text)) { - /* @COMPAT: Remove when log_data_element() is removed. There are - * no other internal code paths that set pcmk__xml_fmt_text. - * Keep an empty case handler so that we don't log an unhandled - * type warning. - */ dump_xml_text(data, options, buffer, depth); } break; @@ -1657,39 +1622,23 @@ pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth) dump_xml_cdata(data, options, buffer, depth); break; default: - crm_warn("Unhandled type: %d", data->type); + crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d", + xml_element_type2str(data->type), data->type); break; - - /* - XML_ATTRIBUTE_NODE = 2 - XML_ENTITY_REF_NODE = 5 - XML_ENTITY_NODE = 6 - XML_PI_NODE = 7 - XML_DOCUMENT_NODE = 9 - XML_DOCUMENT_TYPE_NODE = 10 - XML_DOCUMENT_FRAG_NODE = 11 - XML_NOTATION_NODE = 12 - XML_HTML_DOCUMENT_NODE = 13 - XML_DTD_NODE = 14 - XML_ELEMENT_DECL = 15 - XML_ATTRIBUTE_DECL = 16 - XML_ENTITY_DECL = 17 - XML_NAMESPACE_DECL = 18 - XML_XINCLUDE_START = 19 - XML_XINCLUDE_END = 20 - XML_DOCB_DOCUMENT_NODE = 21 - */ } } char * -dump_xml_formatted_with_text(xmlNode * an_xml_node) +dump_xml_formatted_with_text(const xmlNode *xml) { + /* libxml's xmlNodeDumpOutput() would work here since we're not specifically + * filtering out any nodes. However, use pcmk__xml2text() for consistency, + * to escape attribute values, and to allow a const argument. + */ char *buffer = NULL; GString *g_buffer = g_string_sized_new(1024); - pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty|pcmk__xml_fmt_full, - g_buffer, 0); + pcmk__xml2text(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, g_buffer, 0); pcmk__str_update(&buffer, g_buffer->str); g_string_free(g_buffer, TRUE); @@ -1697,12 +1646,12 @@ dump_xml_formatted_with_text(xmlNode * an_xml_node) } char * -dump_xml_formatted(xmlNode * an_xml_node) +dump_xml_formatted(const xmlNode *xml) { char *buffer = NULL; GString *g_buffer = g_string_sized_new(1024); - pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty, g_buffer, 0); + pcmk__xml2text(xml, pcmk__xml_fmt_pretty, g_buffer, 0); pcmk__str_update(&buffer, g_buffer->str); g_string_free(g_buffer, TRUE); @@ -1710,30 +1659,46 @@ dump_xml_formatted(xmlNode * an_xml_node) } char * -dump_xml_unformatted(xmlNode * an_xml_node) +dump_xml_unformatted(const xmlNode *xml) { char *buffer = NULL; GString *g_buffer = g_string_sized_new(1024); - pcmk__xml2text(an_xml_node, 0, g_buffer, 0); + pcmk__xml2text(xml, 0, g_buffer, 0); pcmk__str_update(&buffer, g_buffer->str); g_string_free(g_buffer, TRUE); return buffer; } -gboolean -xml_has_children(const xmlNode * xml_root) +int +pcmk__xml2fd(int fd, xmlNode *cur) { - if (xml_root != NULL && xml_root->children != NULL) { - return TRUE; + bool success; + + xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL); + CRM_ASSERT(fd_out != NULL); + xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL); + + success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1; + + success = xmlOutputBufferClose(fd_out) != -1 && success; + + if (!success) { + return EIO; } - return FALSE; + + fsync(fd); + return pcmk_rc_ok; } void xml_remove_prop(xmlNode * obj, const char *name) { + if (crm_element_value(obj, name) == NULL) { + return; + } + if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) { crm_trace("Cannot remove %s from %s", name, obj->name); @@ -1750,7 +1715,7 @@ xml_remove_prop(xmlNode * obj, const char *name) } void -save_xml_to_file(xmlNode * xml, const char *desc, const char *filename) +save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename) { char *f = NULL; @@ -1864,7 +1829,7 @@ mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr, old_attr->name, p_old, p_new, element); // Mark document, element, and all element's parents as changed - mark_xml_node_dirty(new_xml); + pcmk__mark_xml_node_dirty(new_xml); // Mark attribute as changed pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_moved); @@ -1886,10 +1851,10 @@ xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml) xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml); while (attr_iter != NULL) { + const char *name = (const char *) attr_iter->name; xmlAttr *old_attr = attr_iter; xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name); - const char *name = (const char *) attr_iter->name; - const char *old_value = crm_element_value(old_xml, name); + const char *old_value = pcmk__xml_attr_value(attr_iter); attr_iter = attr_iter->next; if (new_attr == NULL) { @@ -1943,7 +1908,7 @@ mark_created_attrs(xmlNode *new_xml) const char *attr_name = (const char *) new_attr->name; crm_trace("Created new attribute %s=%s in %s", - attr_name, crm_element_value(new_xml, attr_name), + attr_name, pcmk__xml_attr_value(new_attr), new_xml->name); /* Check ACLs (we can't use the remove-then-create trick because it @@ -2017,7 +1982,7 @@ mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child, crm_trace("Child element %s with id='%s' moved from position %d to %d under %s", new_child->name, (ID(new_child)? ID(new_child) : "<no id>"), p_old, p_new, new_parent->name); - mark_xml_node_dirty(new_parent); + pcmk__mark_xml_node_dirty(new_parent); pcmk__set_xml_flags(nodepriv, pcmk__xf_moved); if (p_old > p_new) { @@ -2102,9 +2067,10 @@ xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml) void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml) { - CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei), + CRM_CHECK((old_xml != NULL) && (new_xml != NULL) + && pcmk__xe_is(old_xml, (const char *) new_xml->name) + && pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_none), return); - CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return); if(xml_tracking_changes(new_xml) == FALSE) { xml_track_changes(new_xml, NULL, NULL, FALSE); @@ -2118,10 +2084,13 @@ can_prune_leaf(xmlNode * xml_node) { xmlNode *cIter = NULL; gboolean can_prune = TRUE; - const char *name = crm_element_name(xml_node); - if (pcmk__strcase_any_of(name, XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF, - XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1, NULL)) { + CRM_CHECK(xml_node != NULL, return FALSE); + + if (pcmk__strcase_any_of((const char *) xml_node->name, + XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF, + XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1, + NULL)) { return FALSE; } @@ -2257,7 +2226,7 @@ pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, return; } - object_name = crm_element_name(update); + object_name = (const char *) update->name; object_href_val = ID(update); if (object_href_val != NULL) { object_href = XML_ATTR_ID; @@ -2294,9 +2263,7 @@ pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, #endif } - CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update), - pcmk__str_casei), - return); + CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return); if (as_diff == FALSE) { /* So that expand_plus_plus() gets called */ @@ -2345,7 +2312,7 @@ update_xml_child(xmlNode * child, xmlNode * to_update) CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(to_update != NULL, return FALSE); - if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) { + if (!pcmk__xe_is(to_update, (const char *) child->name)) { can_update = FALSE; } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) { @@ -2379,7 +2346,7 @@ find_xml_children(xmlNode ** children, xmlNode * root, CRM_CHECK(root != NULL, return FALSE); CRM_CHECK(children != NULL, return FALSE); - if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) { + if ((tag != NULL) && !pcmk__xe_is(root, tag)) { } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) { @@ -2422,7 +2389,7 @@ replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) { can_delete = TRUE; } - if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) { + if (!pcmk__xe_is(update, (const char *) child->name)) { can_delete = FALSE; } if (can_delete && delete_only) { @@ -2444,23 +2411,23 @@ replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean free_xml(child); } else { - xmlNode *tmp = copy_xml(update); - xmlDoc *doc = tmp->doc; - xmlNode *old = NULL; + xmlNode *old = child; + xmlNode *new = xmlCopyNode(update, 1); - xml_accept_changes(tmp); - old = xmlReplaceNode(child, tmp); + CRM_ASSERT(new != NULL); - if(xml_tracking_changes(tmp)) { - /* Replaced sections may have included relevant ACLs */ - pcmk__apply_acl(tmp); - } + // May be unnecessary but avoids slight changes to some test outputs + reset_xml_node_flags(new); - xml_calculate_changes(old, tmp); - xmlDocSetRootElement(doc, old); - free_xml(old); + old = xmlReplaceNode(old, new); + + if (xml_tracking_changes(new)) { + // Replaced sections may have included relevant ACLs + pcmk__apply_acl(new); + } + xml_calculate_changes(old, new); + xmlFreeNode(old); } - child = NULL; return TRUE; } else if (can_delete) { @@ -2491,14 +2458,10 @@ sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive) xmlNode *child = NULL; GSList *nvpairs = NULL; xmlNode *result = NULL; - const char *name = NULL; CRM_CHECK(input != NULL, return NULL); - name = crm_element_name(input); - CRM_CHECK(name != NULL, return NULL); - - result = create_xml_node(parent, name); + result = create_xml_node(parent, (const char *) input->name); nvpairs = pcmk_xml_attrs2nvpairs(input); nvpairs = pcmk_sort_nvpairs(nvpairs); pcmk_nvpairs2xml_attrs(nvpairs, result); @@ -2547,10 +2510,9 @@ xmlNode * crm_next_same_xml(const xmlNode *sibling) { xmlNode *match = pcmk__xe_next(sibling); - const char *name = crm_element_name(sibling); while (match != NULL) { - if (!strcmp(crm_element_name(match), name)) { + if (pcmk__xe_is(match, (const char *) sibling->name)) { return match; } match = pcmk__xe_next(match); @@ -2592,7 +2554,6 @@ crm_xml_cleanup(void) xmlNode * expand_idref(xmlNode * input, xmlNode * top) { - const char *tag = NULL; const char *ref = NULL; xmlNode *result = input; @@ -2603,12 +2564,10 @@ expand_idref(xmlNode * input, xmlNode * top) top = input; } - tag = crm_element_name(result); ref = crm_element_value(result, XML_ATTR_IDREF); - if (ref != NULL) { char *xpath_string = crm_strdup_printf("//%s[@" XML_ATTR_ID "='%s']", - tag, ref); + result->name, ref); result = get_xpath_object(xpath_string, top, LOG_ERR); if (result == NULL) { @@ -2630,7 +2589,7 @@ pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns) char *ret = NULL; if (base == NULL) { - base = getenv("PCMK_schema_directory"); + base = pcmk__env_option(PCMK__ENV_SCHEMA_DIRECTORY); } if (pcmk__str_empty(base)) { base = CRM_SCHEMA_DIRECTORY; @@ -2741,6 +2700,21 @@ crm_destroy_xml(gpointer data) free_xml(data); } +xmlDoc * +getDocPtr(xmlNode *node) +{ + xmlDoc *doc = NULL; + + CRM_CHECK(node != NULL, return NULL); + + doc = node->doc; + if (doc == NULL) { + doc = xmlNewDoc((pcmkXmlStr) "1.0"); + xmlDocSetRootElement(doc, node); + } + return doc; +} + int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child) { @@ -2749,5 +2723,14 @@ add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child) return 1; } +gboolean +xml_has_children(const xmlNode * xml_root) +{ + if (xml_root != NULL && xml_root->children != NULL) { + return TRUE; + } + return FALSE; +} + // LCOV_EXCL_STOP // End deprecated API |