This source file includes following definitions.
- 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
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
33
34
35
36
37
38 static char *
39 decompress_file(const char *filename)
40 {
41 char *buffer = NULL;
42 int rc = pcmk_rc_ok;
43 size_t length = 0;
44 BZFILE *bz_file = NULL;
45 FILE *input = fopen(filename, "r");
46
47 if (input == NULL) {
48 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
49 return NULL;
50 }
51
52 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
53 rc = pcmk__bzlib2rc(rc);
54 if (rc != pcmk_rc_ok) {
55 crm_err("Could not prepare to read compressed %s: %s "
56 QB_XS " rc=%d", filename, pcmk_rc_str(rc), rc);
57 goto done;
58 }
59
60
61
62 do {
63 int read_len = 0;
64
65 buffer = pcmk__realloc(buffer, length + PCMK__BUFFER_SIZE + 1);
66 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
67
68 if ((rc == BZ_OK) || (rc == BZ_STREAM_END)) {
69 crm_trace("Read %ld bytes from file: %d", (long) read_len, rc);
70 length += read_len;
71 }
72 } while (rc == BZ_OK);
73
74 rc = pcmk__bzlib2rc(rc);
75 if (rc != pcmk_rc_ok) {
76 rc = pcmk__bzlib2rc(rc);
77 crm_err("Could not read compressed %s: %s " QB_XS " rc=%d",
78 filename, pcmk_rc_str(rc), rc);
79 free(buffer);
80 buffer = NULL;
81 } else {
82 buffer[length] = '\0';
83 }
84
85 done:
86 BZ2_bzReadClose(&rc, bz_file);
87 fclose(input);
88 return buffer;
89 }
90
91
92
93
94
95
96
97
98
99
100
101 xmlNode *
102 pcmk__xml_read(const char *filename)
103 {
104 bool use_stdin = pcmk__str_eq(filename, "-", pcmk__str_null_matches);
105 xmlNode *xml = NULL;
106 xmlDoc *output = NULL;
107 xmlParserCtxt *ctxt = NULL;
108 const xmlError *last_error = NULL;
109
110
111 ctxt = xmlNewParserCtxt();
112 CRM_CHECK(ctxt != NULL, return NULL);
113
114 xmlCtxtResetLastError(ctxt);
115 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
116
117 if (use_stdin) {
118 output = xmlCtxtReadFd(ctxt, STDIN_FILENO, NULL, NULL,
119 XML_PARSE_NOBLANKS);
120
121 } else if (pcmk__ends_with_ext(filename, ".bz2")) {
122 char *input = decompress_file(filename);
123
124 if (input != NULL) {
125 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
126 XML_PARSE_NOBLANKS);
127 free(input);
128 }
129
130 } else {
131 output = xmlCtxtReadFile(ctxt, filename, NULL, XML_PARSE_NOBLANKS);
132 }
133
134 if (output != NULL) {
135 pcmk__xml_new_private_data((xmlNode *) output);
136 xml = xmlDocGetRootElement(output);
137 if (xml != NULL) {
138
139
140
141
142
143
144 pcmk__strip_xml_text(xml);
145 }
146 }
147
148 last_error = xmlCtxtGetLastError(ctxt);
149 if ((last_error != NULL) && (xml != NULL)) {
150 crm_log_xml_debug(xml, "partial");
151 pcmk__xml_free(xml);
152 xml = NULL;
153 }
154
155 xmlFreeParserCtxt(ctxt);
156 return xml;
157 }
158
159
160
161
162
163
164
165
166
167 xmlNode *
168 pcmk__xml_parse(const char *input)
169 {
170 xmlNode *xml = NULL;
171 xmlDoc *output = NULL;
172 xmlParserCtxt *ctxt = NULL;
173 const xmlError *last_error = NULL;
174
175 if (input == NULL) {
176 return NULL;
177 }
178
179 ctxt = xmlNewParserCtxt();
180 if (ctxt == NULL) {
181 return NULL;
182 }
183
184 xmlCtxtResetLastError(ctxt);
185 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
186
187 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
188 XML_PARSE_NOBLANKS);
189 if (output != NULL) {
190 pcmk__xml_new_private_data((xmlNode *) output);
191 xml = xmlDocGetRootElement(output);
192 }
193
194 last_error = xmlCtxtGetLastError(ctxt);
195 if ((last_error != NULL) && (xml != NULL)) {
196 crm_log_xml_debug(xml, "partial");
197 pcmk__xml_free(xml);
198 xml = NULL;
199 }
200
201 xmlFreeParserCtxt(ctxt);
202 return xml;
203 }
204
205
206
207
208
209
210
211
212
213
214 static void
215 dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
216 int depth)
217 {
218 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
219 bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
220 int spaces = pretty? (2 * depth) : 0;
221
222 for (int lpc = 0; lpc < spaces; lpc++) {
223 g_string_append_c(buffer, ' ');
224 }
225
226 pcmk__g_strcat(buffer, "<", data->name, NULL);
227
228 for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
229 attr = attr->next) {
230
231 if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
232 pcmk__dump_xml_attr(attr, buffer);
233 }
234 }
235
236 if (data->children == NULL) {
237 g_string_append(buffer, "/>");
238
239 } else {
240 g_string_append_c(buffer, '>');
241 }
242
243 if (pretty) {
244 g_string_append_c(buffer, '\n');
245 }
246
247 if (data->children) {
248 for (const xmlNode *child = data->children; child != NULL;
249 child = child->next) {
250 pcmk__xml_string(child, options, buffer, depth + 1);
251 }
252
253 for (int lpc = 0; lpc < spaces; lpc++) {
254 g_string_append_c(buffer, ' ');
255 }
256
257 pcmk__g_strcat(buffer, "</", data->name, ">", NULL);
258
259 if (pretty) {
260 g_string_append_c(buffer, '\n');
261 }
262 }
263 }
264
265
266
267
268
269
270
271
272
273
274 static void
275 dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
276 int depth)
277 {
278 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
279 int spaces = pretty? (2 * depth) : 0;
280 const char *content = (const char *) data->content;
281 gchar *content_esc = NULL;
282
283 if (pcmk__xml_needs_escape(content, pcmk__xml_escape_text)) {
284 content_esc = pcmk__xml_escape(content, pcmk__xml_escape_text);
285 content = content_esc;
286 }
287
288 for (int lpc = 0; lpc < spaces; lpc++) {
289 g_string_append_c(buffer, ' ');
290 }
291
292 g_string_append(buffer, content);
293
294 if (pretty) {
295 g_string_append_c(buffer, '\n');
296 }
297 g_free(content_esc);
298 }
299
300
301
302
303
304
305
306
307
308
309 static void
310 dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
311 int depth)
312 {
313 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
314 int spaces = pretty? (2 * depth) : 0;
315
316 for (int lpc = 0; lpc < spaces; lpc++) {
317 g_string_append_c(buffer, ' ');
318 }
319
320 pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
321 NULL);
322
323 if (pretty) {
324 g_string_append_c(buffer, '\n');
325 }
326 }
327
328
329
330
331
332
333
334
335
336
337 static void
338 dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
339 int depth)
340 {
341 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
342 int spaces = pretty? (2 * depth) : 0;
343
344 for (int lpc = 0; lpc < spaces; lpc++) {
345 g_string_append_c(buffer, ' ');
346 }
347
348 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
349
350 if (pretty) {
351 g_string_append_c(buffer, '\n');
352 }
353 }
354
355
356
357
358
359
360
361
362
363 static const char *
364 xml_element_type_text(xmlElementType type)
365 {
366 static const char *const element_type_names[] = {
367 [XML_ELEMENT_NODE] = "element",
368 [XML_ATTRIBUTE_NODE] = "attribute",
369 [XML_TEXT_NODE] = "text",
370 [XML_CDATA_SECTION_NODE] = "CDATA section",
371 [XML_ENTITY_REF_NODE] = "entity reference",
372 [XML_ENTITY_NODE] = "entity",
373 [XML_PI_NODE] = "PI",
374 [XML_COMMENT_NODE] = "comment",
375 [XML_DOCUMENT_NODE] = "document",
376 [XML_DOCUMENT_TYPE_NODE] = "document type",
377 [XML_DOCUMENT_FRAG_NODE] = "document fragment",
378 [XML_NOTATION_NODE] = "notation",
379 [XML_HTML_DOCUMENT_NODE] = "HTML document",
380 [XML_DTD_NODE] = "DTD",
381 [XML_ELEMENT_DECL] = "element declaration",
382 [XML_ATTRIBUTE_DECL] = "attribute declaration",
383 [XML_ENTITY_DECL] = "entity declaration",
384 [XML_NAMESPACE_DECL] = "namespace declaration",
385 [XML_XINCLUDE_START] = "XInclude start",
386 [XML_XINCLUDE_END] = "XInclude end",
387 };
388
389 if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) {
390 return "unrecognized type";
391 }
392 return element_type_names[type];
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410 void
411 pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer,
412 int depth)
413 {
414 if (data == NULL) {
415 crm_trace("Nothing to dump");
416 return;
417 }
418
419 pcmk__assert(buffer != NULL);
420 CRM_CHECK(depth >= 0, depth = 0);
421
422 switch(data->type) {
423 case XML_ELEMENT_NODE:
424
425 dump_xml_element(data, options, buffer, depth);
426 break;
427 case XML_TEXT_NODE:
428 if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
429 dump_xml_text(data, options, buffer, depth);
430 }
431 break;
432 case XML_COMMENT_NODE:
433 dump_xml_comment(data, options, buffer, depth);
434 break;
435 case XML_CDATA_SECTION_NODE:
436 dump_xml_cdata(data, options, buffer, depth);
437 break;
438 default:
439 crm_warn("Cannot convert XML %s node to text " QB_XS " type=%d",
440 xml_element_type_text(data->type), data->type);
441 break;
442 }
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456 static int
457 write_compressed_stream(char *text, const char *filename, FILE *stream,
458 unsigned int *bytes_out)
459 {
460 unsigned int bytes_in = 0;
461 int rc = pcmk_rc_ok;
462
463
464 BZFILE *bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 0);
465
466 rc = pcmk__bzlib2rc(rc);
467 if (rc != pcmk_rc_ok) {
468 crm_warn("Not compressing %s: could not prepare file stream: %s "
469 QB_XS " rc=%d",
470 filename, pcmk_rc_str(rc), rc);
471 goto done;
472 }
473
474 BZ2_bzWrite(&rc, bz_file, text, strlen(text));
475 rc = pcmk__bzlib2rc(rc);
476 if (rc != pcmk_rc_ok) {
477 crm_warn("Not compressing %s: could not compress data: %s "
478 QB_XS " rc=%d errno=%d",
479 filename, pcmk_rc_str(rc), rc, errno);
480 goto done;
481 }
482
483 BZ2_bzWriteClose(&rc, bz_file, 0, &bytes_in, bytes_out);
484 bz_file = NULL;
485 rc = pcmk__bzlib2rc(rc);
486 if (rc != pcmk_rc_ok) {
487 crm_warn("Not compressing %s: could not write compressed data: %s "
488 QB_XS " rc=%d errno=%d",
489 filename, pcmk_rc_str(rc), rc, errno);
490 goto done;
491 }
492
493 crm_trace("Compressed XML for %s from %u bytes to %u",
494 filename, bytes_in, *bytes_out);
495
496 done:
497 if (bz_file != NULL) {
498 BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL);
499 }
500 return rc;
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514
515 static int
516 write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
517 bool compress)
518 {
519 GString *buffer = g_string_sized_new(1024);
520 unsigned int bytes_out = 0;
521 int rc = pcmk_rc_ok;
522
523 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
524 CRM_CHECK(!pcmk__str_empty(buffer->str),
525 crm_log_xml_info(xml, "dump-failed");
526 rc = pcmk_rc_error;
527 goto done);
528
529 crm_log_xml_trace(xml, "writing");
530
531 if (compress
532 && (write_compressed_stream(buffer->str, filename, stream,
533 &bytes_out) == pcmk_rc_ok)) {
534 goto done;
535 }
536
537 rc = fprintf(stream, "%s", buffer->str);
538 if (rc < 0) {
539 rc = EIO;
540 crm_perror(LOG_ERR, "writing %s", filename);
541 goto done;
542 }
543 bytes_out = (unsigned int) rc;
544 rc = pcmk_rc_ok;
545
546 done:
547 if (fflush(stream) != 0) {
548 rc = errno;
549 crm_perror(LOG_ERR, "flushing %s", filename);
550 }
551
552
553 if ((fsync(fileno(stream)) < 0) && (errno != EROFS) && (errno != EINVAL)) {
554 rc = errno;
555 crm_perror(LOG_ERR, "synchronizing %s", filename);
556 }
557
558 fclose(stream);
559 crm_trace("Saved %u bytes to %s as XML", bytes_out, filename);
560
561 g_string_free(buffer, TRUE);
562 return rc;
563 }
564
565
566
567
568
569
570
571
572
573
574
575 int
576 pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd)
577 {
578 FILE *stream = NULL;
579
580 CRM_CHECK((xml != NULL) && (fd > 0), return EINVAL);
581 stream = fdopen(fd, "w");
582 if (stream == NULL) {
583 return errno;
584 }
585
586 return write_xml_stream(xml, pcmk__s(filename, "unnamed file"), stream,
587 false);
588 }
589
590
591
592
593
594
595
596
597
598
599
600 int
601 pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress)
602 {
603 FILE *stream = NULL;
604
605 CRM_CHECK((xml != NULL) && (filename != NULL), return EINVAL);
606 stream = fopen(filename, "w");
607 if (stream == NULL) {
608 return errno;
609 }
610
611 return write_xml_stream(xml, filename, stream, compress);
612 }
613
614
615
616
617
618
619
620
621
622
623 int
624 pcmk__xml2fd(int fd, xmlNode *cur)
625 {
626 bool success;
627
628 xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL);
629 pcmk__mem_assert(fd_out);
630 xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL);
631
632 success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1;
633
634 success = xmlOutputBufferClose(fd_out) != -1 && success;
635
636 if (!success) {
637 return EIO;
638 }
639
640 fsync(fd);
641 return pcmk_rc_ok;
642 }
643
644 void
645 save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
646 {
647 char *f = NULL;
648
649 if (filename == NULL) {
650 char *uuid = crm_generate_uuid();
651
652 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
653 filename = f;
654 free(uuid);
655 }
656
657 crm_info("Saving %s to %s", desc, filename);
658 pcmk__xml_write_file(xml, filename, false);
659 free(f);
660 }