pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
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 
45 static char *
46 read_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 
78 static char *
79 decompress_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 
125 done:
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 
165 xmlNode *
166 pcmk__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 
243 xmlNode *
244 pcmk__xml_parse(const char *input)
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 
291 static void
292 dump_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 
351 static void
352 dump_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 
386 static void
387 dump_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 
414 static void
415 dump_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 
440 static const char *
441 xml_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 
487 void
488 pcmk__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 
533 static int
534 write_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 
573 done:
574  if (bz_file != NULL) {
575  BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL);
576  }
577  return rc;
578 }
579 
593 static int
594 write_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 
625 done:
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 
659 int
660 pcmk__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 
687 int
688 pcmk__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 
712 int
713 pcmk__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 
733 void
734 save_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 
757 xmlNode *
758 filename2xml(const char *filename)
759 {
760  return pcmk__xml_read(filename);
761 }
762 
763 xmlNode *
765 {
766  return pcmk__xml_read(NULL);
767 }
768 
769 xmlNode *
770 string2xml(const char *input)
771 {
772  return pcmk__xml_parse(input);
773 }
774 
775 char *
776 dump_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 
788 char *
789 dump_xml_formatted_with_text(const xmlNode *xml)
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 
801 char *
802 dump_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 
814 int
815 write_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 
827 int
828 write_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 CRM_CHECK(expr, failure_action)
Definition: logging.h:245
int pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress, unsigned int *nbytes)
Definition: xml_io.c:688
A dumping ground.
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1110
char data[0]
Definition: cpg.c:58
char * crm_generate_uuid(void)
Definition: utils.c:431
int pcmk_rc2legacy(int rc)
Definition: results.c:546
xmlNode * stdin2xml(void)
Definition: xml_io.c:764
char * dump_xml_formatted(const xmlNode *xml)
Definition: xml_io.c:776
void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition: xml_io.c:488
int write_xml_fd(const xmlNode *xml, const char *filename, int fd, gboolean compress)
Definition: xml_io.c:815
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1028
G_GNUC_INTERNAL void pcmk__log_xmllib_err(void *ctx, const char *fmt,...) G_GNUC_PRINTF(2
Exclude certain XML attributes (for calculating digests)
Definition: xml_internal.h:137
int pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd, bool compress, unsigned int *nbytes)
Definition: xml_io.c:660
xmlNode * pcmk__xml_parse(const char *input)
Definition: xml_io.c:244
xmlNode * string2xml(const char *input)
Definition: xml_io.c:770
xmlNode * filename2xml(const char *filename)
Definition: xml_io.c:758
enum crm_ais_msg_types type
Definition: cpg.c:51
Wrappers for and extensions to XML input/output functions.
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
Include indentation and newlines.
Definition: xml_internal.h:140
#define crm_warn(fmt, args...)
Definition: logging.h:394
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:923
#define crm_trace(fmt, args...)
Definition: logging.h:404
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1296
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
Wrappers for and extensions to libxml2.
#define parse_xml_recover(result, fn,...)
Definition: xml_io.c:141
int write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
Definition: xml_io.c:828
#define PCMK__NELEM(a)
Definition: internal.h:48
char * dump_xml_formatted_with_text(const xmlNode *xml)
Definition: xml_io.c:789
xmlNode * pcmk__xml_read(const char *filename)
Definition: xml_io.c:166
#define pcmk__str_copy(str)
int pcmk__xml2fd(int fd, xmlNode *cur)
Definition: xml_io.c:713
const xmlChar * pcmkXmlStr
Definition: xml.h:41
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition: digest.c:232
#define CRM_XS
Definition: logging.h:56
const char * pcmk__get_tmpdir(void)
Definition: io.c:547
int pcmk__bzlib2rc(int bz2)
Map a bz2 return code to the most similar Pacemaker return code.
Definition: results.c:906
#define PCMK__BUFFER_SIZE
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:331
#define crm_err(fmt, args...)
Definition: logging.h:391
#define CRM_ASSERT(expr)
Definition: results.h:42
xmlNode * input
G_GNUC_INTERNAL void pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer)
Definition: xml_attr.c:62
#define pcmk__mem_assert(ptr)
#define crm_log_xml_info(xml, text)
Definition: logging.h:410
Include XML text nodes.
Definition: xml_internal.h:153
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
char * dump_xml_unformatted(const xmlNode *xml)
Definition: xml_io.c:802
Deprecated Pacemaker XML I/O API.
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition: xml_io.c:734
#define crm_info(fmt, args...)
Definition: logging.h:399
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:635