pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
xml_io.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/types.h>
16
17#include <bzlib.h>
18#include <libxml/parser.h>
19#include <libxml/tree.h>
20#include <libxml/xmlIO.h> // xmlOutputBuffer*
21
22#include <crm/crm.h>
23#include <crm/common/xml.h>
24#include <crm/common/xml_io.h>
25#include "crmcommon_private.h"
26
27/* @COMPAT XML_PARSE_RECOVER allows some XML errors to be silently worked around
28 * by libxml2, which is potentially ambiguous and dangerous. We should drop it
29 * when we can break backward compatibility with configurations that might be
30 * relying on it (i.e. pacemaker 3.0.0).
31 */
32#define PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER (XML_PARSE_NOBLANKS)
33#define PCMK__XML_PARSE_OPTS_WITH_RECOVER (XML_PARSE_NOBLANKS \
34 |XML_PARSE_RECOVER)
35
45static char *
46read_stdin(void)
47{
48 char *buf = NULL;
49 size_t length = 0;
50
51 do {
52 buf = pcmk__realloc(buf, length + PCMK__BUFFER_SIZE + 1);
53 length += fread(buf + length, 1, PCMK__BUFFER_SIZE, stdin);
54 } while ((feof(stdin) == 0) && (ferror(stdin) == 0));
55
56 if (ferror(stdin) != 0) {
57 crm_err("Error reading input from stdin");
58 free(buf);
59 buf = NULL;
60 } else {
61 buf[length] = '\0';
62 }
63 clearerr(stdin);
64 return buf;
65}
66
78static char *
79decompress_file(const char *filename)
80{
81 char *buffer = NULL;
82 int rc = pcmk_rc_ok;
83 size_t length = 0;
84 BZFILE *bz_file = NULL;
85 FILE *input = fopen(filename, "r");
86
87 if (input == NULL) {
88 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
89 return NULL;
90 }
91
92 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
93 rc = pcmk__bzlib2rc(rc);
94 if (rc != pcmk_rc_ok) {
95 crm_err("Could not prepare to read compressed %s: %s "
96 CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc);
97 goto done;
98 }
99
100 // cppcheck seems not to understand the abort-logic in pcmk__realloc
101 // cppcheck-suppress memleak
102 do {
103 int read_len = 0;
104
105 buffer = pcmk__realloc(buffer, length + PCMK__BUFFER_SIZE + 1);
106 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
107
108 if ((rc == BZ_OK) || (rc == BZ_STREAM_END)) {
109 crm_trace("Read %ld bytes from file: %d", (long) read_len, rc);
110 length += read_len;
111 }
112 } while (rc == BZ_OK);
113
114 rc = pcmk__bzlib2rc(rc);
115 if (rc != pcmk_rc_ok) {
116 rc = pcmk__bzlib2rc(rc);
117 crm_err("Could not read compressed %s: %s " CRM_XS " rc=%d",
118 filename, pcmk_rc_str(rc), rc);
119 free(buffer);
120 buffer = NULL;
121 } else {
122 buffer[length] = '\0';
123 }
124
125done:
126 BZ2_bzReadClose(&rc, bz_file);
127 fclose(input);
128 return buffer;
129}
130
131// @COMPAT Remove macro at 3.0.0 when we drop XML_PARSE_RECOVER
141#define parse_xml_recover(result, fn, ...) do { \
142 *result = fn(__VA_ARGS__, PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); \
143 if (*result == NULL) { \
144 *result = fn(__VA_ARGS__, PCMK__XML_PARSE_OPTS_WITH_RECOVER); \
145 \
146 if (*result != NULL) { \
147 crm_warn("Successfully recovered from XML errors " \
148 "(note: a future release will treat this as a " \
149 "fatal failure)"); \
150 } \
151 } \
152 } while (0);
153
165xmlNode *
166pcmk__xml_read(const char *filename)
167{
168 bool use_stdin = pcmk__str_eq(filename, "-", pcmk__str_null_matches);
169 xmlNode *xml = NULL;
170 xmlDoc *output = NULL;
171 xmlParserCtxt *ctxt = NULL;
172 const xmlError *last_error = NULL;
173
174 // Create a parser context
175 ctxt = xmlNewParserCtxt();
176 CRM_CHECK(ctxt != NULL, return NULL);
177
178 xmlCtxtResetLastError(ctxt);
179 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
180
181 if (use_stdin) {
182 /* @COMPAT After dropping XML_PARSE_RECOVER, we can avoid capturing
183 * stdin into a buffer and instead call
184 * xmlCtxtReadFd(ctxt, STDIN_FILENO, NULL, NULL, XML_PARSE_NOBLANKS);
185 *
186 * For now we have to save the input so that we can use it twice.
187 */
188 char *input = read_stdin();
189
190 if (input != NULL) {
191 parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input,
192 NULL, NULL);
193 free(input);
194 }
195
196 } else if (pcmk__ends_with_ext(filename, ".bz2")) {
197 char *input = decompress_file(filename);
198
199 if (input != NULL) {
200 parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input,
201 NULL, NULL);
202 free(input);
203 }
204
205 } else {
206 parse_xml_recover(&output, xmlCtxtReadFile, ctxt, filename, NULL);
207 }
208
209 if (output != NULL) {
210 xml = xmlDocGetRootElement(output);
211 if (xml != NULL) {
212 /* @TODO Should we really be stripping out text? This seems like an
213 * overly broad way to get rid of whitespace, if that's the goal.
214 * Text nodes may be invalid in most or all Pacemaker inputs, but
215 * stripping them in a generic "parse XML from file" function may
216 * not be the best way to ignore them.
217 */
219 }
220 }
221
222 // @COMPAT At 3.0.0, free xml and return NULL if xml != NULL on error
223 last_error = xmlCtxtGetLastError(ctxt);
224 if (last_error != NULL) {
225 if (xml != NULL) {
226 crm_log_xml_info(xml, "Partial");
227 }
228 }
229
230 xmlFreeParserCtxt(ctxt);
231 return xml;
232}
233
243xmlNode *
245{
246 xmlNode *xml = NULL;
247 xmlDoc *output = NULL;
248 xmlParserCtxt *ctxt = NULL;
249 const xmlError *last_error = NULL;
250
251 if (input == NULL) {
252 return NULL;
253 }
254
255 ctxt = xmlNewParserCtxt();
256 if (ctxt == NULL) {
257 return NULL;
258 }
259
260 xmlCtxtResetLastError(ctxt);
261 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
262
263 parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input, NULL,
264 NULL);
265
266 if (output != NULL) {
267 xml = xmlDocGetRootElement(output);
268 }
269
270 // @COMPAT At 3.0.0, free xml and return NULL if xml != NULL; update doxygen
271 last_error = xmlCtxtGetLastError(ctxt);
272 if (last_error != NULL) {
273 if (xml != NULL) {
274 crm_log_xml_info(xml, "Partial");
275 }
276 }
277
278 xmlFreeParserCtxt(ctxt);
279 return xml;
280}
281
291static void
292dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
293 int depth)
294{
295 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
296 bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
297 int spaces = pretty? (2 * depth) : 0;
298
299 for (int lpc = 0; lpc < spaces; lpc++) {
300 g_string_append_c(buffer, ' ');
301 }
302
303 pcmk__g_strcat(buffer, "<", data->name, NULL);
304
305 for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
306 attr = attr->next) {
307
308 if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
309 pcmk__dump_xml_attr(attr, buffer);
310 }
311 }
312
313 if (data->children == NULL) {
314 g_string_append(buffer, "/>");
315
316 } else {
317 g_string_append_c(buffer, '>');
318 }
319
320 if (pretty) {
321 g_string_append_c(buffer, '\n');
322 }
323
324 if (data->children) {
325 for (const xmlNode *child = data->children; child != NULL;
326 child = child->next) {
327 pcmk__xml_string(child, options, buffer, depth + 1);
328 }
329
330 for (int lpc = 0; lpc < spaces; lpc++) {
331 g_string_append_c(buffer, ' ');
332 }
333
334 pcmk__g_strcat(buffer, "</", data->name, ">", NULL);
335
336 if (pretty) {
337 g_string_append_c(buffer, '\n');
338 }
339 }
340}
341
351static void
352dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
353 int depth)
354{
355 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
356 int spaces = pretty? (2 * depth) : 0;
357 const char *content = (const char *) data->content;
358 gchar *content_esc = NULL;
359
361 content_esc = pcmk__xml_escape(content, pcmk__xml_escape_text);
362 content = content_esc;
363 }
364
365 for (int lpc = 0; lpc < spaces; lpc++) {
366 g_string_append_c(buffer, ' ');
367 }
368
369 g_string_append(buffer, content);
370
371 if (pretty) {
372 g_string_append_c(buffer, '\n');
373 }
374 g_free(content_esc);
375}
376
386static void
387dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
388 int depth)
389{
390 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
391 int spaces = pretty? (2 * depth) : 0;
392
393 for (int lpc = 0; lpc < spaces; lpc++) {
394 g_string_append_c(buffer, ' ');
395 }
396
397 pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
398 NULL);
399
400 if (pretty) {
401 g_string_append_c(buffer, '\n');
402 }
403}
404
414static void
415dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
416 int depth)
417{
418 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
419 int spaces = pretty? (2 * depth) : 0;
420
421 for (int lpc = 0; lpc < spaces; lpc++) {
422 g_string_append_c(buffer, ' ');
423 }
424
425 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
426
427 if (pretty) {
428 g_string_append_c(buffer, '\n');
429 }
430}
431
440static const char *
441xml_element_type_text(xmlElementType type)
442{
443 static const char *const element_type_names[] = {
444 [XML_ELEMENT_NODE] = "element",
445 [XML_ATTRIBUTE_NODE] = "attribute",
446 [XML_TEXT_NODE] = "text",
447 [XML_CDATA_SECTION_NODE] = "CDATA section",
448 [XML_ENTITY_REF_NODE] = "entity reference",
449 [XML_ENTITY_NODE] = "entity",
450 [XML_PI_NODE] = "PI",
451 [XML_COMMENT_NODE] = "comment",
452 [XML_DOCUMENT_NODE] = "document",
453 [XML_DOCUMENT_TYPE_NODE] = "document type",
454 [XML_DOCUMENT_FRAG_NODE] = "document fragment",
455 [XML_NOTATION_NODE] = "notation",
456 [XML_HTML_DOCUMENT_NODE] = "HTML document",
457 [XML_DTD_NODE] = "DTD",
458 [XML_ELEMENT_DECL] = "element declaration",
459 [XML_ATTRIBUTE_DECL] = "attribute declaration",
460 [XML_ENTITY_DECL] = "entity declaration",
461 [XML_NAMESPACE_DECL] = "namespace declaration",
462 [XML_XINCLUDE_START] = "XInclude start",
463 [XML_XINCLUDE_END] = "XInclude end",
464 };
465
466 if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) {
467 return "unrecognized type";
468 }
469 return element_type_names[type];
470}
471
487void
488pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer,
489 int depth)
490{
491 if (data == NULL) {
492 crm_trace("Nothing to dump");
493 return;
494 }
495
496 CRM_ASSERT(buffer != NULL);
497 CRM_CHECK(depth >= 0, depth = 0);
498
499 switch(data->type) {
500 case XML_ELEMENT_NODE:
501 /* Handle below */
502 dump_xml_element(data, options, buffer, depth);
503 break;
504 case XML_TEXT_NODE:
505 if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
506 dump_xml_text(data, options, buffer, depth);
507 }
508 break;
509 case XML_COMMENT_NODE:
510 dump_xml_comment(data, options, buffer, depth);
511 break;
512 case XML_CDATA_SECTION_NODE:
513 dump_xml_cdata(data, options, buffer, depth);
514 break;
515 default:
516 crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d",
517 xml_element_type_text(data->type), data->type);
518 break;
519 }
520}
521
533static int
534write_compressed_stream(char *text, const char *filename, FILE *stream,
535 unsigned int *bytes_out)
536{
537 unsigned int bytes_in = 0;
538 int rc = pcmk_rc_ok;
539
540 // (5, 0, 0): (intermediate block size, silent, default workFactor)
541 BZFILE *bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 0);
542
543 rc = pcmk__bzlib2rc(rc);
544 if (rc != pcmk_rc_ok) {
545 crm_warn("Not compressing %s: could not prepare file stream: %s "
546 CRM_XS " rc=%d",
547 filename, pcmk_rc_str(rc), rc);
548 goto done;
549 }
550
551 BZ2_bzWrite(&rc, bz_file, text, strlen(text));
552 rc = pcmk__bzlib2rc(rc);
553 if (rc != pcmk_rc_ok) {
554 crm_warn("Not compressing %s: could not compress data: %s "
555 CRM_XS " rc=%d errno=%d",
556 filename, pcmk_rc_str(rc), rc, errno);
557 goto done;
558 }
559
560 BZ2_bzWriteClose(&rc, bz_file, 0, &bytes_in, bytes_out);
561 bz_file = NULL;
562 rc = pcmk__bzlib2rc(rc);
563 if (rc != pcmk_rc_ok) {
564 crm_warn("Not compressing %s: could not write compressed data: %s "
565 CRM_XS " rc=%d errno=%d",
566 filename, pcmk_rc_str(rc), rc, errno);
567 goto done;
568 }
569
570 crm_trace("Compressed XML for %s from %u bytes to %u",
571 filename, bytes_in, *bytes_out);
572
573done:
574 if (bz_file != NULL) {
575 BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL);
576 }
577 return rc;
578}
579
593static int
594write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
595 bool compress, unsigned int *nbytes)
596{
597 // @COMPAT Drop nbytes as arg when we drop write_xml_fd()/write_xml_file()
598 GString *buffer = g_string_sized_new(1024);
599 unsigned int bytes_out = 0;
600 int rc = pcmk_rc_ok;
601
602 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
603 CRM_CHECK(!pcmk__str_empty(buffer->str),
604 crm_log_xml_info(xml, "dump-failed");
605 rc = pcmk_rc_error;
606 goto done);
607
608 crm_log_xml_trace(xml, "writing");
609
610 if (compress
611 && (write_compressed_stream(buffer->str, filename, stream,
612 &bytes_out) == pcmk_rc_ok)) {
613 goto done;
614 }
615
616 rc = fprintf(stream, "%s", buffer->str);
617 if (rc < 0) {
618 rc = EIO;
619 crm_perror(LOG_ERR, "writing %s", filename);
620 goto done;
621 }
622 bytes_out = (unsigned int) rc;
623 rc = pcmk_rc_ok;
624
625done:
626 if (fflush(stream) != 0) {
627 rc = errno;
628 crm_perror(LOG_ERR, "flushing %s", filename);
629 }
630
631 // Don't report error if the file does not support synchronization
632 if ((fsync(fileno(stream)) < 0) && (errno != EROFS) && (errno != EINVAL)) {
633 rc = errno;
634 crm_perror(LOG_ERR, "synchronizing %s", filename);
635 }
636
637 fclose(stream);
638 crm_trace("Saved %u bytes to %s as XML", bytes_out, filename);
639
640 if (nbytes != NULL) {
641 *nbytes = bytes_out;
642 }
643 g_string_free(buffer, TRUE);
644 return rc;
645}
646
659int
660pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd,
661 bool compress, unsigned int *nbytes)
662{
663 // @COMPAT Drop compress and nbytes arguments when we drop write_xml_fd()
664 FILE *stream = NULL;
665
666 CRM_CHECK((xml != NULL) && (fd > 0), return EINVAL);
667 stream = fdopen(fd, "w");
668 if (stream == NULL) {
669 return errno;
670 }
671
672 return write_xml_stream(xml, pcmk__s(filename, "unnamed file"), stream,
673 compress, nbytes);
674}
675
687int
688pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress,
689 unsigned int *nbytes)
690{
691 // @COMPAT Drop nbytes argument when we drop write_xml_fd()
692 FILE *stream = NULL;
693
694 CRM_CHECK((xml != NULL) && (filename != NULL), return EINVAL);
695 stream = fopen(filename, "w");
696 if (stream == NULL) {
697 return errno;
698 }
699
700 return write_xml_stream(xml, filename, stream, compress, nbytes);
701}
702
712int
713pcmk__xml2fd(int fd, xmlNode *cur)
714{
715 bool success;
716
717 xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL);
718 pcmk__mem_assert(fd_out);
719 xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL);
720
721 success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1;
722
723 success = xmlOutputBufferClose(fd_out) != -1 && success;
724
725 if (!success) {
726 return EIO;
727 }
728
729 fsync(fd);
730 return pcmk_rc_ok;
731}
732
733void
734save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
735{
736 char *f = NULL;
737
738 if (filename == NULL) {
739 char *uuid = crm_generate_uuid();
740
741 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
742 filename = f;
743 free(uuid);
744 }
745
746 crm_info("Saving %s to %s", desc, filename);
747 pcmk__xml_write_file(xml, filename, false, NULL);
748 free(f);
749}
750
751
752// Deprecated functions kept only for backward API compatibility
753// LCOV_EXCL_START
754
756
757xmlNode *
758filename2xml(const char *filename)
759{
760 return pcmk__xml_read(filename);
761}
762
763xmlNode *
765{
766 return pcmk__xml_read(NULL);
767}
768
769xmlNode *
770string2xml(const char *input)
771{
772 return pcmk__xml_parse(input);
773}
774
775char *
776dump_xml_formatted(const xmlNode *xml)
777{
778 char *str = NULL;
779 GString *buffer = g_string_sized_new(1024);
780
781 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
782
783 str = pcmk__str_copy(buffer->str);
784 g_string_free(buffer, TRUE);
785 return str;
786}
787
788char *
790{
791 char *str = NULL;
792 GString *buffer = g_string_sized_new(1024);
793
795
796 str = pcmk__str_copy(buffer->str);
797 g_string_free(buffer, TRUE);
798 return str;
799}
800
801char *
802dump_xml_unformatted(const xmlNode *xml)
803{
804 char *str = NULL;
805 GString *buffer = g_string_sized_new(1024);
806
807 pcmk__xml_string(xml, 0, buffer, 0);
808
809 str = pcmk__str_copy(buffer->str);
810 g_string_free(buffer, TRUE);
811 return str;
812}
813
814int
815write_xml_fd(const xmlNode *xml, const char *filename, int fd,
816 gboolean compress)
817{
818 unsigned int nbytes = 0;
819 int rc = pcmk__xml_write_fd(xml, filename, fd, compress, &nbytes);
820
821 if (rc != pcmk_rc_ok) {
822 return pcmk_rc2legacy(rc);
823 }
824 return (int) nbytes;
825}
826
827int
828write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
829{
830 unsigned int nbytes = 0;
831 int rc = pcmk__xml_write_file(xml, filename, compress, &nbytes);
832
833 if (rc != pcmk_rc_ok) {
834 return pcmk_rc2legacy(rc);
835 }
836 return (int) nbytes;
837}
838
839// LCOV_EXCL_STOP
840// End deprecated API
#define PCMK__NELEM(a)
Definition internal.h:48
char * crm_generate_uuid(void)
Definition utils.c:431
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.
Definition util.h:98
enum crm_ais_msg_types type
Definition cpg.c:3
char data[0]
Definition cpg.c:10
A dumping ground.
#define PCMK__BUFFER_SIZE
G_GNUC_INTERNAL void pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer)
Definition xml_attr.c:62
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition digest.c:232
G_GNUC_INTERNAL void pcmk__log_xmllib_err(void *ctx, const char *fmt,...) G_GNUC_PRINTF(2
const char * pcmk__get_tmpdir(void)
Definition io.c:547
#define crm_log_xml_info(xml, text)
Definition logging.h:408
#define crm_info(fmt, args...)
Definition logging.h:397
#define crm_warn(fmt, args...)
Definition logging.h:392
#define CRM_XS
Definition logging.h:56
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:331
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
xmlNode * input
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_error
Definition results.h:157
int pcmk_rc2legacy(int rc)
Definition results.c:546
#define pcmk__mem_assert(ptr)
int pcmk__bzlib2rc(int bz2)
Map a bz2 return code to the most similar Pacemaker return code.
Definition results.c:906
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:635
@ pcmk__str_null_matches
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1296
#define pcmk__str_copy(str)
Wrappers for and extensions to libxml2.
const xmlChar * pcmkXmlStr
Definition xml.h:41
@ pcmk__xml_fmt_pretty
Include indentation and newlines.
@ pcmk__xml_fmt_filtered
Exclude certain XML attributes (for calculating digests)
@ pcmk__xml_fmt_text
Include XML text nodes.
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:1028
void pcmk__strip_xml_text(xmlNode *xml)
Definition xml.c:923
@ pcmk__xml_escape_text
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:1110
int write_xml_fd(const xmlNode *xml, const char *filename, int fd, gboolean compress)
Definition xml_io.c:815
int pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress, unsigned int *nbytes)
Definition xml_io.c:688
xmlNode * filename2xml(const char *filename)
Definition xml_io.c:758
char * dump_xml_formatted_with_text(const xmlNode *xml)
Definition xml_io.c:789
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition xml_io.c:734
int write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
Definition xml_io.c:828
void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition xml_io.c:488
xmlNode * pcmk__xml_read(const char *filename)
Definition xml_io.c:166
xmlNode * string2xml(const char *input)
Definition xml_io.c:770
xmlNode * stdin2xml(void)
Definition xml_io.c:764
int pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd, bool compress, unsigned int *nbytes)
Definition xml_io.c:660
int pcmk__xml2fd(int fd, xmlNode *cur)
Definition xml_io.c:713
#define parse_xml_recover(result, fn,...)
Definition xml_io.c:141
char * dump_xml_unformatted(const xmlNode *xml)
Definition xml_io.c:802
xmlNode * pcmk__xml_parse(const char *input)
Definition xml_io.c:244
char * dump_xml_formatted(const xmlNode *xml)
Definition xml_io.c:776
Wrappers for and extensions to XML input/output functions.
Deprecated Pacemaker XML I/O API.