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 pcmk__xml_new_private_data((xmlNode *) output);
211 xml = xmlDocGetRootElement(output);
212 if (xml != NULL) {
213
214
215
216
217
218
219 pcmk__strip_xml_text(xml);
220 }
221 }
222
223
224 last_error = xmlCtxtGetLastError(ctxt);
225 if (last_error != NULL) {
226 if (xml != NULL) {
227 crm_log_xml_info(xml, "Partial");
228 }
229 }
230
231 xmlFreeParserCtxt(ctxt);
232 return xml;
233 }
234
235
236
237
238
239
240
241
242
243
244 xmlNode *
245 pcmk__xml_parse(const char *input)
246 {
247 xmlNode *xml = NULL;
248 xmlDoc *output = NULL;
249 xmlParserCtxt *ctxt = NULL;
250 const xmlError *last_error = NULL;
251
252 if (input == NULL) {
253 return NULL;
254 }
255
256 ctxt = xmlNewParserCtxt();
257 if (ctxt == NULL) {
258 return NULL;
259 }
260
261 xmlCtxtResetLastError(ctxt);
262 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
263
264 parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input, NULL,
265 NULL);
266
267 if (output != NULL) {
268 pcmk__xml_new_private_data((xmlNode *) output);
269 xml = xmlDocGetRootElement(output);
270 }
271
272
273 last_error = xmlCtxtGetLastError(ctxt);
274 if (last_error != NULL) {
275 if (xml != NULL) {
276 crm_log_xml_info(xml, "Partial");
277 }
278 }
279
280 xmlFreeParserCtxt(ctxt);
281 return xml;
282 }
283
284
285
286
287
288
289
290
291
292
293 static void
294 dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
295 int depth)
296 {
297 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
298 bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
299 int spaces = pretty? (2 * depth) : 0;
300
301 for (int lpc = 0; lpc < spaces; lpc++) {
302 g_string_append_c(buffer, ' ');
303 }
304
305 pcmk__g_strcat(buffer, "<", data->name, NULL);
306
307 for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
308 attr = attr->next) {
309
310 if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
311 pcmk__dump_xml_attr(attr, buffer);
312 }
313 }
314
315 if (data->children == NULL) {
316 g_string_append(buffer, "/>");
317
318 } else {
319 g_string_append_c(buffer, '>');
320 }
321
322 if (pretty) {
323 g_string_append_c(buffer, '\n');
324 }
325
326 if (data->children) {
327 for (const xmlNode *child = data->children; child != NULL;
328 child = child->next) {
329 pcmk__xml_string(child, options, buffer, depth + 1);
330 }
331
332 for (int lpc = 0; lpc < spaces; lpc++) {
333 g_string_append_c(buffer, ' ');
334 }
335
336 pcmk__g_strcat(buffer, "</", data->name, ">", NULL);
337
338 if (pretty) {
339 g_string_append_c(buffer, '\n');
340 }
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353 static void
354 dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
355 int depth)
356 {
357 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
358 int spaces = pretty? (2 * depth) : 0;
359 const char *content = (const char *) data->content;
360 gchar *content_esc = NULL;
361
362 if (pcmk__xml_needs_escape(content, pcmk__xml_escape_text)) {
363 content_esc = pcmk__xml_escape(content, pcmk__xml_escape_text);
364 content = content_esc;
365 }
366
367 for (int lpc = 0; lpc < spaces; lpc++) {
368 g_string_append_c(buffer, ' ');
369 }
370
371 g_string_append(buffer, content);
372
373 if (pretty) {
374 g_string_append_c(buffer, '\n');
375 }
376 g_free(content_esc);
377 }
378
379
380
381
382
383
384
385
386
387
388 static void
389 dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
390 int depth)
391 {
392 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
393 int spaces = pretty? (2 * depth) : 0;
394
395 for (int lpc = 0; lpc < spaces; lpc++) {
396 g_string_append_c(buffer, ' ');
397 }
398
399 pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
400 NULL);
401
402 if (pretty) {
403 g_string_append_c(buffer, '\n');
404 }
405 }
406
407
408
409
410
411
412
413
414
415
416 static void
417 dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
418 int depth)
419 {
420 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
421 int spaces = pretty? (2 * depth) : 0;
422
423 for (int lpc = 0; lpc < spaces; lpc++) {
424 g_string_append_c(buffer, ' ');
425 }
426
427 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
428
429 if (pretty) {
430 g_string_append_c(buffer, '\n');
431 }
432 }
433
434
435
436
437
438
439
440
441
442 static const char *
443 xml_element_type_text(xmlElementType type)
444 {
445 static const char *const element_type_names[] = {
446 [XML_ELEMENT_NODE] = "element",
447 [XML_ATTRIBUTE_NODE] = "attribute",
448 [XML_TEXT_NODE] = "text",
449 [XML_CDATA_SECTION_NODE] = "CDATA section",
450 [XML_ENTITY_REF_NODE] = "entity reference",
451 [XML_ENTITY_NODE] = "entity",
452 [XML_PI_NODE] = "PI",
453 [XML_COMMENT_NODE] = "comment",
454 [XML_DOCUMENT_NODE] = "document",
455 [XML_DOCUMENT_TYPE_NODE] = "document type",
456 [XML_DOCUMENT_FRAG_NODE] = "document fragment",
457 [XML_NOTATION_NODE] = "notation",
458 [XML_HTML_DOCUMENT_NODE] = "HTML document",
459 [XML_DTD_NODE] = "DTD",
460 [XML_ELEMENT_DECL] = "element declaration",
461 [XML_ATTRIBUTE_DECL] = "attribute declaration",
462 [XML_ENTITY_DECL] = "entity declaration",
463 [XML_NAMESPACE_DECL] = "namespace declaration",
464 [XML_XINCLUDE_START] = "XInclude start",
465 [XML_XINCLUDE_END] = "XInclude end",
466 };
467
468 if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) {
469 return "unrecognized type";
470 }
471 return element_type_names[type];
472 }
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489 void
490 pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer,
491 int depth)
492 {
493 if (data == NULL) {
494 crm_trace("Nothing to dump");
495 return;
496 }
497
498 pcmk__assert(buffer != NULL);
499 CRM_CHECK(depth >= 0, depth = 0);
500
501 switch(data->type) {
502 case XML_ELEMENT_NODE:
503
504 dump_xml_element(data, options, buffer, depth);
505 break;
506 case XML_TEXT_NODE:
507 if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
508 dump_xml_text(data, options, buffer, depth);
509 }
510 break;
511 case XML_COMMENT_NODE:
512 dump_xml_comment(data, options, buffer, depth);
513 break;
514 case XML_CDATA_SECTION_NODE:
515 dump_xml_cdata(data, options, buffer, depth);
516 break;
517 default:
518 crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d",
519 xml_element_type_text(data->type), data->type);
520 break;
521 }
522 }
523
524
525
526
527
528
529
530
531
532
533
534
535 static int
536 write_compressed_stream(char *text, const char *filename, FILE *stream,
537 unsigned int *bytes_out)
538 {
539 unsigned int bytes_in = 0;
540 int rc = pcmk_rc_ok;
541
542
543 BZFILE *bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 0);
544
545 rc = pcmk__bzlib2rc(rc);
546 if (rc != pcmk_rc_ok) {
547 crm_warn("Not compressing %s: could not prepare file stream: %s "
548 CRM_XS " rc=%d",
549 filename, pcmk_rc_str(rc), rc);
550 goto done;
551 }
552
553 BZ2_bzWrite(&rc, bz_file, text, strlen(text));
554 rc = pcmk__bzlib2rc(rc);
555 if (rc != pcmk_rc_ok) {
556 crm_warn("Not compressing %s: could not compress data: %s "
557 CRM_XS " rc=%d errno=%d",
558 filename, pcmk_rc_str(rc), rc, errno);
559 goto done;
560 }
561
562 BZ2_bzWriteClose(&rc, bz_file, 0, &bytes_in, bytes_out);
563 bz_file = NULL;
564 rc = pcmk__bzlib2rc(rc);
565 if (rc != pcmk_rc_ok) {
566 crm_warn("Not compressing %s: could not write compressed data: %s "
567 CRM_XS " rc=%d errno=%d",
568 filename, pcmk_rc_str(rc), rc, errno);
569 goto done;
570 }
571
572 crm_trace("Compressed XML for %s from %u bytes to %u",
573 filename, bytes_in, *bytes_out);
574
575 done:
576 if (bz_file != NULL) {
577 BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL);
578 }
579 return rc;
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595 static int
596 write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
597 bool compress, unsigned int *nbytes)
598 {
599
600 GString *buffer = g_string_sized_new(1024);
601 unsigned int bytes_out = 0;
602 int rc = pcmk_rc_ok;
603
604 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
605 CRM_CHECK(!pcmk__str_empty(buffer->str),
606 crm_log_xml_info(xml, "dump-failed");
607 rc = pcmk_rc_error;
608 goto done);
609
610 crm_log_xml_trace(xml, "writing");
611
612 if (compress
613 && (write_compressed_stream(buffer->str, filename, stream,
614 &bytes_out) == pcmk_rc_ok)) {
615 goto done;
616 }
617
618 rc = fprintf(stream, "%s", buffer->str);
619 if (rc < 0) {
620 rc = EIO;
621 crm_perror(LOG_ERR, "writing %s", filename);
622 goto done;
623 }
624 bytes_out = (unsigned int) rc;
625 rc = pcmk_rc_ok;
626
627 done:
628 if (fflush(stream) != 0) {
629 rc = errno;
630 crm_perror(LOG_ERR, "flushing %s", filename);
631 }
632
633
634 if ((fsync(fileno(stream)) < 0) && (errno != EROFS) && (errno != EINVAL)) {
635 rc = errno;
636 crm_perror(LOG_ERR, "synchronizing %s", filename);
637 }
638
639 fclose(stream);
640 crm_trace("Saved %u bytes to %s as XML", bytes_out, filename);
641
642 if (nbytes != NULL) {
643 *nbytes = bytes_out;
644 }
645 g_string_free(buffer, TRUE);
646 return rc;
647 }
648
649
650
651
652
653
654
655
656
657
658
659
660
661 int
662 pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd,
663 bool compress, unsigned int *nbytes)
664 {
665
666 FILE *stream = NULL;
667
668 CRM_CHECK((xml != NULL) && (fd > 0), return EINVAL);
669 stream = fdopen(fd, "w");
670 if (stream == NULL) {
671 return errno;
672 }
673
674 return write_xml_stream(xml, pcmk__s(filename, "unnamed file"), stream,
675 compress, nbytes);
676 }
677
678
679
680
681
682
683
684
685
686
687
688
689 int
690 pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress,
691 unsigned int *nbytes)
692 {
693
694 FILE *stream = NULL;
695
696 CRM_CHECK((xml != NULL) && (filename != NULL), return EINVAL);
697 stream = fopen(filename, "w");
698 if (stream == NULL) {
699 return errno;
700 }
701
702 return write_xml_stream(xml, filename, stream, compress, nbytes);
703 }
704
705
706
707
708
709
710
711
712
713
714 int
715 pcmk__xml2fd(int fd, xmlNode *cur)
716 {
717 bool success;
718
719 xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL);
720 pcmk__mem_assert(fd_out);
721 xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL);
722
723 success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1;
724
725 success = xmlOutputBufferClose(fd_out) != -1 && success;
726
727 if (!success) {
728 return EIO;
729 }
730
731 fsync(fd);
732 return pcmk_rc_ok;
733 }
734
735 void
736 save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
737 {
738 char *f = NULL;
739
740 if (filename == NULL) {
741 char *uuid = crm_generate_uuid();
742
743 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
744 filename = f;
745 free(uuid);
746 }
747
748 crm_info("Saving %s to %s", desc, filename);
749 pcmk__xml_write_file(xml, filename, false, NULL);
750 free(f);
751 }
752
753
754
755
756
757 #include <crm/common/xml_io_compat.h>
758
759 xmlNode *
760 filename2xml(const char *filename)
761 {
762 return pcmk__xml_read(filename);
763 }
764
765 xmlNode *
766 stdin2xml(void)
767 {
768 return pcmk__xml_read(NULL);
769 }
770
771 xmlNode *
772 string2xml(const char *input)
773 {
774 return pcmk__xml_parse(input);
775 }
776
777 char *
778 dump_xml_formatted(const xmlNode *xml)
779 {
780 char *str = NULL;
781 GString *buffer = g_string_sized_new(1024);
782
783 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
784
785 str = pcmk__str_copy(buffer->str);
786 g_string_free(buffer, TRUE);
787 return str;
788 }
789
790 char *
791 dump_xml_formatted_with_text(const xmlNode *xml)
792 {
793 char *str = NULL;
794 GString *buffer = g_string_sized_new(1024);
795
796 pcmk__xml_string(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buffer, 0);
797
798 str = pcmk__str_copy(buffer->str);
799 g_string_free(buffer, TRUE);
800 return str;
801 }
802
803 char *
804 dump_xml_unformatted(const xmlNode *xml)
805 {
806 char *str = NULL;
807 GString *buffer = g_string_sized_new(1024);
808
809 pcmk__xml_string(xml, 0, buffer, 0);
810
811 str = pcmk__str_copy(buffer->str);
812 g_string_free(buffer, TRUE);
813 return str;
814 }
815
816 int
817 write_xml_fd(const xmlNode *xml, const char *filename, int fd,
818 gboolean compress)
819 {
820 unsigned int nbytes = 0;
821 int rc = pcmk__xml_write_fd(xml, filename, fd, compress, &nbytes);
822
823 if (rc != pcmk_rc_ok) {
824 return pcmk_rc2legacy(rc);
825 }
826 return (int) nbytes;
827 }
828
829 int
830 write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
831 {
832 unsigned int nbytes = 0;
833 int rc = pcmk__xml_write_file(xml, filename, compress, &nbytes);
834
835 if (rc != pcmk_rc_ok) {
836 return pcmk_rc2legacy(rc);
837 }
838 return (int) nbytes;
839 }
840
841
842