This source file includes following definitions.
- read_stdin
- decompress_file
- pcmk__xml_read
- pcmk__xml_parse
- dump_xml_element
- dump_xml_text
- dump_xml_cdata
- dump_xml_comment
- xml_element_type_text
- pcmk__xml_string
- write_compressed_stream
- write_xml_stream
- pcmk__xml_write_fd
- pcmk__xml_write_file
- pcmk__xml2fd
- save_xml_to_file
- filename2xml
- stdin2xml
- string2xml
- dump_xml_formatted
- dump_xml_formatted_with_text
- dump_xml_unformatted
- write_xml_fd
- write_xml_file
1
2
3
4
5
6
7
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>
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
28
29
30
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
36
37
38
39
40
41
42
43
44
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
67
68
69
70
71
72
73
74
75
76
77
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
101
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
132
133
134
135
136
137
138
139
140
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
154
155
156
157
158
159
160
161
162
163
164
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
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
183
184
185
186
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
213
214
215
216
217
218 pcmk__strip_xml_text(xml);
219 }
220 }
221
222
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
234
235
236
237
238
239
240
241
242
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
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
282
283
284
285
286
287
288
289
290
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
342
343
344
345
346
347
348
349
350
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
360 if (pcmk__xml_needs_escape(content, pcmk__xml_escape_text)) {
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
377
378
379
380
381
382
383
384
385
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
405
406
407
408
409
410
411
412
413
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
432
433
434
435
436
437
438
439
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
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
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
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
522
523
524
525
526
527
528
529
530
531
532
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
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
580
581
582
583
584
585
586
587
588
589
590
591
592
593 static int
594 write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
595 bool compress, unsigned int *nbytes)
596 {
597
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
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
647
648
649
650
651
652
653
654
655
656
657
658
659 int
660 pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd,
661 bool compress, unsigned int *nbytes)
662 {
663
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
676
677
678
679
680
681
682
683
684
685
686
687 int
688 pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress,
689 unsigned int *nbytes)
690 {
691
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
703
704
705
706
707
708
709
710
711
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
753
754
755 #include <crm/common/xml_io_compat.h>
756
757 xmlNode *
758 filename2xml(const char *filename)
759 {
760 return pcmk__xml_read(filename);
761 }
762
763 xmlNode *
764 stdin2xml(void)
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
794 pcmk__xml_string(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buffer, 0);
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
840